local _ENV = hf_setenv('kpqz_nntable',true)

----------------------------------------------------------------
local skynet                            = require "skynet"
local utils                             = require "utils"
local proxypack                         = require "proxypack"
local msgdef                            = require "msgdef"
local pb                                = require "protobuf"
local util                              = require "util"
local table_def                         = require "table_def"
local sharedata                         = require "skynet.sharedata"
local error_code                        = require "error_code"
local reason                            = require "reason"
--local kpqz                              = require "niuniu.kpqz"
local cocall                            = require "cocall"
local constant                          = require "constant"
local nn_robot                          = require "niuniu.kpqz_robot"
local system_store                      = require "system_store"
local game_def                          = require "game_def"
local cjson                             = require "cjson"
local cards                             = require "cards"
local expand = require("expand") --老游戏功能拓展



local table_insert                      = table.insert
local table_remove                      = table.remove
local table_sort                        = table.sort
local table_unpack                      = table.unpack
local math_floor                        = math.floor
local math_random                       = math.random
local math_abs                          = math.abs
local string_format                     = string.format
local math_min = math.min


local GAME_TIME_LOAD_ROBOT              = 5
local COCALL_TIMEOUT                    = 5
local PALYER_STATUS_SITDOWN             = 1
local PALYER_STATUS_READY               = 2
local PALYER_STATUS_WATCH               = 3
local PLAYER_STATUS_PLAYING             = 4

local STANDUP_NOT_ENOUGH_COINS          = 1
local STANDUP_NOT_OPERATE               = 2
local STANDUP_OVER_COINS                = 3

local MIN_PLAYER_NUMBER                 = 2

local OFFLINE_CLIENT_FD                 = -1
local ROBOT_CLIENT_FD                   = -2
local REGISTER_CLIENT_FD                = 0

local GAME_STATUS = {
    GAME_STATUS_INIT                  = 0,
    GAME_STATUS_WAIT_START            = 1,
    GAME_STATUS_START                 = 2,
    GAME_STATUS_BET                   = 3,
    GAME_STATUS_CACL_CATTLE           = 4,
    GAME_STATUS_SHOW_AND_SETTLE       = 5,
    GAME_STATUS_LOAD_ROBOT            = 6,
    GAME_STATUS_CHECK_START           = 7,  
}

local GAME_TIME_WAIT_START              = 3
local GAME_TIME_START                   = 6
local GAME_TIME_BET                     = 7
local GAME_TIME_CACL_CATTLE             = 5
local GAME_TIME_SHOW_AND_SETTLE         = 9

--最大牌的倍數
local MAX_CARD_TIMES                    = 3
-- local GAME_TIME_WAIT_START           = 2
-- local GAME_TIME_START                = 2
-- local GAME_TIME_BET                  = 2
-- local GAME_TIME_CACL_CATTLE          = 2
-- local GAME_TIME_SHOW_AND_SETTLE      = 2

local CONTROL_RESULT_WIN = 1
local CONTROL_RESULT_LOSE = 2

local handler                           = {}
local internal                          = {}

local get_random_robot_uid = nil

function init(params)
    kpqz = params.kpqz
    global_configs = nil
    player_info_list = {}
    curr_locked_uids = {}
    uid_game_session_map = {}  --标记房间所有玩家的clientId 包括观战者的clientId
    seated_position_uid_map = {}  --坐下的玩家的顺序
    noseat_players_list = {}  --无座玩家的映射
    robot_manager = {}

    card_type_times_map = {}
    sorted_can_bet_times_list = {}
    grab_banker_times_dizhu_map = {}
    two_round_action_record_map = {}

    game_bet_result_list = {}
    curr_kpqz_instance = nil
    curr_round = nil
    curr_round_need_player = nil
    curr_status = nil
    self_table_type = nil
    self_table_id = nil
    self_table_gid = nil
    self_game_config = nil
    self_table_pattern = nil
    max_seat_num = 0

    -- 进入过本房间但是没开过局的玩家列表
    players_never_start = {}

    closing_server = false
    stopping_server = false         --停止服务器
    on_matching = false
    status_change_flag = false
    TABLE_STATUS_STRING = {}
    pattern_player_uid = false       --受控制用户id
    affect_count = 0          --用户影响额度
    affect_rate = 0          --用户输赢影响概率
    will_pattern_leave = false      --受控制玩家是否离开房间

    game_start_init_time = nil
    game_start_wait_start_time = nil
    game_start_start_time = nil
    game_start_bet_time = nil
    game_start_calc_cattle_time = nil
    game_start_show_and_settle_time = nil

    tablesvr_id = nil
    self_game_type = nil

    --enter_robot_map = {}
    system_send_award = false   --系统放水

    -- control_uid = nil           --权重最高的玩家
    control_result = 0          --控制玩家输赢 1:赢 2：输
    --触发财神驾到的金额
    caishen_coins_map = {}
    player_register_time_map = {} --玩家注册时间,REQ_ENTER后清掉
    is_curr_have_robot = false
    gm_store_coins_add = 0

    system_result_type = 0 --系统控制类型
    gm_control_uid     = 0 --gm控制玩家uid

    hf_set_sentinel_end(_ENV)
end

local function load_config()
    local attr = {}
    global_configs = nil
    global_configs = setmetatable({},{
        __index = function(t,k) 
            if attr[k] then
                return attr[k]
            else
                local v = sharedata.deepcopy(k)
                attr[k] = v
                return v
            end
        end
    })
    _G.global_configs = global_configs
end

local function send_to_gateway(uid, client_fd, ...)
    if not client_fd or client_fd <= 0 then
        return
    end

    return utils.send_to_gateway(0, uid, client_fd, ...)
end

local function notify_others(action, excepted_uid, msg)
    --先广播给桌子上的人
    for uid, fd in pairs(uid_game_session_map) do
        if uid ~= excepted_uid then
            send_to_gateway(uid, fd, action, msg)
        end
    end
end

local function notify_all(action, msg)
    return notify_others(action, nil, msg)
end 

--把输赢发送到好友服(平台游戏概况)
local function sendLoseWinToFriendServer(system_lose_win)  
    if system_lose_win == 0 then return end             
    R().friendsvr(1):send('.info_mgr', 'set_room_summary', self_table_type, {table_type = self_table_type, lose_win = system_lose_win})
end  

function return_comma_split(str, split_char)
    local sub_str_tab = utils.str_split(str, split_char)
    for k,v in pairs(sub_str_tab) do
       sub_str_tab[k] = tonumber(v)
    end
    table_sort(sub_str_tab)

    return sub_str_tab
end

local function get_extra_bill_info(uid)
    local t = { game_type = self_game_type, table_type = self_table_type }
    local player_info = player_info_list[uid]

    if not player_info then
        return t
    end

    t.client_version = player_info.client_version
    t.channel = player_info.channel
    t.cur_channel = player_info.cur_channel
    t.device_id = player_info.device_id
    t.device_brand = player_info.device_brand
    t.os_system = player_info.os_system
    t.platform = player_info.platform
    
    return t
end

local function get_seated_num()
    local num = 0

    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            num = num + 1
        end
    end

    return num
end

local function is_robot(uid)
    return robot_manager[uid] and true
end

local function is_on_match()
    return on_matching
end

local function get_real_player_seated_num()
    local num = 0

    for position, uid in pairs(seated_position_uid_map) do
        if uid and not is_robot(uid) then
            num = num + 1
        end
    end

    return num
end

local function get_all_player_num()
    local num = get_seated_num()
    return num + #noseat_players_list
end

local function watch_session(game_session, uid, observing)
    if game_session <= 0 then
        return
    end

    local gateway_id = game_session >> 31
    local request

    if observing then
        request = "observe_fd"
    else
        request = "unobserve_fd"
    end

    R().gateway(gateway_id):send(".watchdog", "tablesvr", request, R().get_source(), game_session, uid)
end

local function game_session_correct(uid, game_session)
    if uid_game_session_map[uid] ~= game_session then
        errlog(string_format("uid_game_session_map[%d](%s) ~= game_session(%d)", uid, tostring(uid_game_session_map[uid]), game_session))
        return false
    end

    return true
end

--玩这实时输赢op
function realTimeWinLose(uid, leave_time)
    if is_robot(uid) then return end
    local player_info = player_info_list[uid]
    if not player_info or not player_info.enter_time then 
        errlog("__________player_info.enter_time__是空的____", uid, player_info)
        return 
    end    
    local table_gid = player_info.enter_time .. "_" .. self_table_id .."_"..uid
    leave_time = leave_time or 0
    if player_info.all_win_lose == 0 and player_info.all_free == 0 then 
        --没产生流水就不记录了
        return
    end
    local data = {
        op = "user_leave_game",
        uid = uid,
        table_gid = table_gid, --唯一标记
        table_type = self_table_type,
        name = player_info.name,        
        enter_coins = player_info.enter_coins, --进入时身上金币
        enter_safe_coins = player_info.enter_safe_coins, --进入时保险箱金币 
        enter_time = player_info.enter_time, --进入时间
        leave_time = leave_time, --离开时间
        leave_coins = player_info.coins, --离开时身上金币
        leave_safe_coins = player_info.safe_box, --离开时保险箱金币
        win_lose = player_info.all_win_lose, --在玩游戏内产生的输赢（
        free = player_info.all_free, --玩游戏交上的台费
    }
    dbglog("_______user_leave_game_____",  data)
    billlog(data)
end

local function unlock_one_player(uid)
    if is_robot(uid) then return end
    realTimeWinLose(uid, os.time())
    R().exdbsvr(1):send(".tlock_mgr", "unset_on_table", uid, self_table_gid)    
    R().friendsvr(1):send(".info_mgr","update_info", uid, { curr_game_type = 0 })
    curr_locked_uids[uid] = nil
end

local function clear_player_action_record(uid)
    for _, action_record_map in pairs(two_round_action_record_map) do
        action_record_map[uid] = nil
    end

  --  dbglog(string_format("uid(%d) clear operate record ok", uid))
    return
end

local function remove_from_noseat(uid)
    local noseat_idx

    for i, suid in ipairs(noseat_players_list) do
        if suid == uid then
            noseat_idx = i
            break
        end
    end

    if noseat_idx then
        local remove_uid = table_remove(noseat_players_list, noseat_idx)
 --       dbglog(string_format("2==================>uid(%d) remove_uid(%s)", uid, tostring(remove_uid)))
        return uid == remove_uid
    else
        return false
    end
end

local function clear_player_info(uid)
    uid_game_session_map[uid] = nil
    player_info_list[uid] = nil
    remove_from_noseat(uid)
 --   dbglog("-----------call----remove_from_noseat-------------------")
    clear_player_action_record(uid)
end

local function get_player_ip_list()
    local player_ip_list = {}
    for uid,player_data in pairs(player_info_list) do
        if player_data.login_ip then
            table_insert(player_ip_list,player_data.login_ip)
        end
    end
    return player_ip_list
end

local function get_seated_robot_num()
    local robot_num = 0
    for _, uid in pairs(seated_position_uid_map) do
        if uid and is_robot(uid) then
            robot_num = robot_num + 1
        end
    end

    return robot_num
end

local function get_seated_human_num()
 --   print("get_seated_human_num===========",tostring_r(seated_position_uid_map),tostring_r(uid_game_session_map))
    local human_num = 0
    for _, uid in pairs(seated_position_uid_map) do
        if uid and not is_robot(uid) then
            human_num = human_num + 1
        end
    end

    return human_num
end

local function notify_matchsvr_table_stats(is_delete)
    local table_stats = {}
    if is_delete then
        table_stats.table_gid = self_table_gid
        table_stats.table_type = self_table_type
        R().newmatchsvr(1):send(".table_mgr", "delete_table_stats", table_stats)
    else
        table_stats.table_type = self_table_type
        table_stats.left_num = table_def.game_player_num[self_game_type] - get_seated_num()
        table_stats.table_gid = self_table_gid
        table_stats.robot_num = get_seated_robot_num()
        table_stats.human_num = get_seated_human_num()
        table_stats.players_never_start = players_never_start
        table_stats.table_pattern = self_table_pattern
        table_stats.player_ip_list = get_player_ip_list()
        R().newmatchsvr(1):send(".table_mgr", "update_table_stats", table_stats)
    --    dbglog(string_format("table_stats(%s)", tostring_r(table_stats)))
    end
end

local function set_player_status(uid, status)
    local player_info = player_info_list[uid]
    player_info.status = status
end

local function add_to_noseat(uid)    
    for _,uid1 in pairs(noseat_players_list) do
        if uid1 == uid then
            return
        end
    end
    
    table_insert(noseat_players_list, uid)
    set_player_status(uid, PALYER_STATUS_WATCH)
 --   dbglog(string_format("1==================>noseat_players_list(%s) uid(%d)", tostring_r(noseat_players_list), uid))
end

local function deal_stand_up(uid, op_code)
    local player_info = player_info_list[uid]

    seated_position_uid_map[player_info.position] = false
    player_info.position = nil   
    add_to_noseat(uid)
  --  dbglog("-----------call----add_to_noseat-------------------")
    clear_player_action_record(uid)

    notify_all("kpqz.NTF_STANDUP", { uid = uid, reason = op_code })

    notify_matchsvr_table_stats()
end


local function deal_robot_leave(uid)
    local player_info = player_info_list[uid]
    if player_info.position then
        deal_stand_up(uid, 0)
    end
    clear_player_info(uid)
    notify_matchsvr_table_stats()
    if robot_manager[uid] then
        robot_manager[uid]['on_exit'](robot_manager[uid])
        robot_manager[uid] = nil
    end
    --notify_all("kpqz.NTF_LEAVE", { uid = uid })
end

local function clear_and_unlock_offline_player(uid)
    --dbglog(string_format("uid(%d) clear offlined player", uid))

    if is_robot(uid) then
        deal_robot_leave(uid)
        return
    end

    unlock_one_player(uid)

    local player_info = player_info_list[uid]

    if player_info.position then
        deal_stand_up(uid, 0)
    end

    clear_player_info(uid)
end

function deal_player_regular_leave(uid)
-- deal_player_regular_leave = function(uid)
    if is_robot(uid) then
        deal_robot_leave(uid)
        return
    end

    local game_session = uid_game_session_map[uid]
    clear_and_unlock_offline_player(uid)

    notify_matchsvr_table_stats()
    skynet.send(".table_mgr", "lua", "leave", uid) 
end

local function check_player_info_exist(uid)
    local player_info = player_info_list[uid]

    if not player_info then
        errlog(string_format("uid(%d) no player info", uid))
        return false, error_code.NO_SUCH_PLAYER
    end

    return true
end

local function system_do_banker(uid)
    return uid == constant.SYSTEM_DO_BANKER_UID
end

function get_own_coins_from_svr(uid)
    local ok, own_coins = R().basesvr({ key = uid }):call(".msg_handler",  "get_coins",  uid)

    if not ok then
        errlog(string_format("call basesvr get_coins error uid(%d)", uid))
        return false
    end

    return true, own_coins
end

function check_sit_down_coins(uid, own_coins)
    if self_game_config.min <= 0 then
        return true
    end

    if own_coins < self_game_config.min then
        errlog("own_coins < min", uid, own_coins, self_game_config.min)
        return false, error_code.GOLD_IS_NOT_ENOUGH
    end

	if self_game_config.max ~= 0 and own_coins > self_game_config.max then
		return false, error_code.GOLD_OVER_LIMIT
	end

    return true
end

local function get_rsp_player_info(player_info)
    return { 
        uid = player_info.uid, 
        name = player_info.name, 
        coins = player_info.coins, 
        icon = player_info.icon, 
        position = player_info.position, 
        sex = player_info.sex,
        vip_level = player_info.vip_level,
        icon_border = player_info.icon_border,
        status = player_info.status,
     }
end

local function deal_sit_down(uid, position)
    local player_info = player_info_list[uid]
 --   print("deal_sit_down======",uid,type(uid),position, tostring_r(player_info),tostring_r(player_info_list))

    seated_position_uid_map[position] = uid
    player_info.position = position
    player_info.status = PALYER_STATUS_SITDOWN
    remove_from_noseat(uid)
  --  dbglog("-----------call----remove_from_noseat-------------------")

    notify_all("kpqz.NTF_SITDOWN", { player = get_rsp_player_info(player_info) })

    notify_matchsvr_table_stats()
end

local function get_next_can_sit_position()
    local can_position = 0

    for position, uid in pairs(seated_position_uid_map) do
        if not uid then
            can_position = position
            break
        end
    end

    return can_position
end

function internal.get_player_num()
    return get_seated_num()
end

local function deal_auto_sit_down(player_info)
    local uid = player_info.uid
    local own_coins = player_info.coins
    local can_position = get_next_can_sit_position()

--[[
	if uid == 170 then
		can_position = 1
	end

	if uid == 171 then
		can_position = 2
	end
--]]


    if can_position == 0 then
        dbglog(string_format("uid(%d) can not sitdown", uid))
        return false, error_code.CANT_SITDOWN
    end

    local ok, ret = check_sit_down_coins(uid, own_coins)
    if not ok then
        return false, ret
    end

    deal_sit_down(uid, can_position)
    return true
end

local function update_curr_round_need_player()
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata.open_robot then
        return
    end
    
    local robot_cnf = global_configs.robot[roomdata.robot_type or 0] or {}
    local round_player_rate = robot_cnf.num_rate or {[3] = 40,[4] = 50,[5] = 10}

    local rand_num = math_random(0,100)
    local tmp_value = 0
    for key,value in pairs(round_player_rate) do
        tmp_value = tmp_value + value
        if rand_num <= tmp_value then
            curr_round_need_player = key
            break
        end
    end
end

local function lock_one_player(uid)
    local ok, succ = R().exdbsvr(1):call(".tlock_mgr", "set_on_table", uid, self_table_gid, self_table_gid)
    if not ok then
        errlog(string_format("uid(%d) self_table_gid(%d) failed to set_on_table", uid, self_table_gid))
        return false
    end

    if not succ then
        return false
    end

    curr_locked_uids[uid] = true
    return true
end




local function clear_offline_players()
    for uid, status in pairs(uid_game_session_map) do
        if status == OFFLINE_CLIENT_FD then
            local ok, ret = xpcall(clear_and_unlock_offline_player, debug.traceback, uid)
            if not ok then
                errlog(uid, ret)
            end
        end
    end
end

local function get_player_enter_data(uid)
    local ok, base_data = R().basesvr{ key = uid }:call(".msg_handler", "get_base_data", uid)
    if not ok then
        errlog(string_format("uid(%d) call basesvr get_base_data error", uid))
        return
    end

    local ok, enter_data = R().hallsvr{ key = uid }:call(".msg_handler", "get_enter_data", uid)
    if not ok then
        errlog(string_format("uid(%d) call hallsvr get_enter_data error", uid))
        enter_data = {}
    end

   -- dbglog(string_format("base_data(%s) enter_data(%s)", tostring_r(base_data), tostring_r(enter_data)))

    local player_info = {
        uid = base_data.uid, 
        name = enter_data.name or "",
        coins = base_data.coins, 
        icon = enter_data.icon or "", 
        sex = enter_data.sex, 
        vip_level = base_data.vip_level or 0,
        icon_border = enter_data.icon_border,
        safe_box = base_data.safe_box,
     }

    return player_info
end

local function get_rate_list()
    local rate_list = {}

    for type, times in pairs(kpqz.card_type_times_map) do
        table_insert(rate_list, { type = type, times = times })
    end 

    return rate_list
end

local function get_my_bet_list(uid)
    local my_bet_list = {}

    if not curr_kpqz_instance then
        return my_bet_list
    end

    local tmp_my_list = curr_kpqz_instance:get_my_bet_list(uid)

    if tmp_my_list then
        for side, bet_coins in pairs(tmp_my_list) do
            table_insert(my_bet_list, { side = side, bet_coins = bet_coins })
        end
    end

    return my_bet_list
end

local function get_rsp_seat_player_info_list()
    local rsp_list = {}

    for position, uid in pairs(seated_position_uid_map) do
        if player_info_list[uid] then
            local player_info = player_info_list[uid]
            local rsp_player_info = get_rsp_player_info(player_info)
            table_insert(rsp_list, rsp_player_info)
        end
    end

    return rsp_list
end

local function get_rsp_player_info_list()
    local rsp_list = {}

    for uid, player_info in pairs(player_info_list) do
        local rsp_player_info = get_rsp_player_info(player_info)
        table_insert(rsp_list, rsp_player_info)
    end

    return rsp_list
end

local function get_seated_list()
    local seated_uid_list = {}

    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            local play_info = player_info_list[uid]
            table_insert(seated_uid_list, play_info)
        end
    end

    return seated_uid_list
end

local function get_table_info()
     local table_info = {}

     table_info.dizhu = self_game_config.dizhu
     table_info.cost = self_game_config.cost
     table_info.max_seat_num = max_seat_num
     table_info.dizhu = self_game_config.dizhu
     table_info.table_name = self_game_config.name or ""

     return table_info
end

local function get_enter_info()
    local enter_info = {}
   
    enter_info.table_info = get_table_info()
    return enter_info
end

local function trigger_event(action,...)
    for _, robot_obj in pairs(robot_manager) do
        local f = robot_obj[action]
        if f then
            f(robot_obj, ...)
        end
    end
end

local function notify_player_enter(uid)
    local player_info = player_info_list[uid]
    local player = get_rsp_player_info(player_info)
   
    local play_effect = false
    local enter_word = ""
    local vip_level_conf = utils.getDataBykv(global_configs.vip,"level",player_info.vip_level or 0)
    if vip_level_conf and vip_level_conf.privilege[game_def.VIP_PRIVILEGE_TYPE_SIX] then
        play_effect = true
        enter_word = vip_level_conf.enter_word or ""
    end
    if curr_kpqz_instance and curr_kpqz_instance:check_player_in_round(uid) then
        play_effect = false
    end
    notify_all('kpqz.NTF_PLAYER_ENTER',{play_effect = play_effect,player = player,enter_word = enter_word})
end

local function deal_enter(uid, client_fd, msg)
--    dbglog(string_format("uid(%d) client_fd(%d) enter", uid, client_fd))

    if not uid_game_session_map[uid] then
        errlog(string_format("uid(%d) had not been registerd", uid))
        return false, -431
    end

    uid_game_session_map[uid] = client_fd
    player_register_time_map[uid] = nil

    if client_fd == ROBOT_CLIENT_FD then
  --      dbglog(string_format("robot enter uid(%d)", uid))
        --enter_robot_map[uid] = true
        return true
    end
    
    local data = get_player_enter_data(uid)
    
    local player_info = player_info_list[uid]
    if not player_info then
        errlog(uid,'kpqz_nntable failed to get player_info')
        return false
    end

    if data then
        player_info.name = data.name
        player_info.icon = data.icon
        player_info.coins = data.coins
        player_info.vip_level = data.vip_level
        player_info.icon_border = data.icon_border
        player_info.safe_box = data.safe_box
        player_info.enter_coins = data.coins
        player_info.enter_time = os.time()
        player_info.enter_safe_coins = data.safe_box
        player_info.all_win_lose = 0
        player_info.all_free = 0
        player_info.all_produce = 0
        player_info.all_bet = 0
        
    else
        errlog(string_format("uid(%d) failed to get enter data", uid))
    end
    player_info.os_system = data.os_system
    player_info.platform = data.platform
    player_info.client_version = msg.client_version
    player_info.channel = msg.channel
    player_info.cur_channel = msg.cur_channel
    player_info.device_id = msg.device_id
    player_info.device_brand = msg.device_brand    

    if player_info.position then 
        dbglog(string_format("uid(%d) have seat position(%d)", player_info.uid, player_info.position))
        return true
    end

	add_to_noseat(uid)
    deal_auto_sit_down(player_info)
    
    return true
end

local function player_num_meet_condition()
    if get_seated_num() >= MIN_PLAYER_NUMBER then
        return true
    end

    return false
end

local function update_player_coins(uid, coins)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = coins
    else
        errlog(string_format("uid(%d) coins(%d) player info not exist", uid, coins))
    end
end

local function add_coins(uid, coins, reason, ...)
    local ok, succ, ret = R().basesvr{ key = uid }:call(".msg_handler","add_coins", uid, coins, reason, ...)
    if not ok then
        errlog(string_format("failed to add coins uid(%d) coins(%d)", uid, coins))
        return false
    end
    if not succ then
        errlog(string_format("failed to add coins uid(%d) coins(%d)", uid, coins))
        return false
    end

	R().basesvr{key=uid}:send('.msg_handler','sub_lose_coins', uid, coins)

    update_player_coins(uid, ret.curr)
    local fd = assert(uid_game_session_map[uid])    
    utils.notify_money_changed(uid, { coins = ret.curr }, fd)
    return ret
end

local function reduce_coins(uid, money, r, ...)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "reduce_coins_to_empty", uid, money, r, ...)
    if not ok then
        errlog(string_format("uid(%d) call basesvr reduce coins error", uid))
        return false
    end

    if not succ then
        errlog(string_format("uid(%d) basesvr reduce coins succ == false error", uid))
        return false
    end


	if r ~= reason.PAY_COMMISSION  then
		R().basesvr{key=uid}:send('.msg_handler','add_lose_coins', uid, money)
	end

    update_player_coins(uid, ret.curr)
    local fd = uid_game_session_map[uid]
    if fd then
        utils.notify_money_changed(uid, {coins = ret.curr},fd)
    end
    
    return ret
end

local function get_player_curr_coins(uid)
    if player_info_list[uid] then
        return player_info_list[uid].coins

    else
        local ok,base_data = R().basesvr({key=uid}):call('.msg_handler','get_base_data',uid)
        if not ok then
            errlog(uid,'get_player_curr_coins-->failed to get base_data')
            return 0
        end

        return base_data.coins
    end

    return 0
end

local function change_robot_store(system_win)
    local roomdata = global_configs.roomdata[self_table_type] or {}
    if not roomdata.robot_type then
        errlog("change_robot_store_conf_error")
        return
    end

    if system_win >= 0 then
        R().exdbsvr(1):send('.system_coins_mgr','add_system_coins',self_game_type,self_table_type,system_win)
    else
        if system_send_award then --从奖励库存扣钱
            R().exdbsvr(1):send('.system_coins_mgr','reduce_award_coins',self_game_type,self_table_type,math.abs(system_win))
        else --从系统库存扣钱
            R().exdbsvr(1):send('.system_coins_mgr','sub_system_coins',self_game_type,self_table_type,math.abs(system_win))
        end
    end
end

local function write_card_record_log(bet_result_map, pay_fee_map)
    local winners = {}
    local losers = {}

    local player_win = 0
    local add_score_map = {}
    local total_num = 0
    local change_store_coins = 0
    gm_store_coins_add = 0
    for uid, uid_bet_result in pairs(bet_result_map) do
        local player_info = player_info_list[uid]
        local left_coins = 0

        if player_info then
            left_coins = player_info.coins
        end

        if uid_bet_result.result_coins > 0 then
            table_insert(winners, { 
                uid = uid,  
                left_score = left_coins,  
                add_score = uid_bet_result.result_coins,  
                pay_fee = pay_fee_map[uid],  
                is_robot = is_robot(uid),  
                is_banker = curr_kpqz_instance:is_banker(uid)
            })
        else
            table_insert(losers, { 
                uid = uid,  
                left_score = left_coins,  
                add_score = uid_bet_result.result_coins,  
                pay_fee  = pay_fee_map[uid],  
                is_robot = is_robot(uid),
                is_banker = curr_kpqz_instance:is_banker(uid)
            })
        end

        add_score_map[uid] = uid_bet_result.result_coins
        if not is_robot(uid) then
            player_win = player_win + uid_bet_result.result_coins + math_abs(pay_fee_map[uid] or 0)
            change_store_coins = change_store_coins + uid_bet_result.result_coins + math_abs(pay_fee_map[uid] or 0)
            if (gm_control_uid or 0) > 0 and uid == (gm_control_uid or 0) then
                local win_coins = uid_bet_result.result_coins + math_abs(pay_fee_map[uid] or 0)
                print("system_result============222",uid,system_result,win_coins)
                if system_result_type == constant.PERSONAL_STORE_WIN and win_coins > 0 then
                    local ok,coins = R().basesvr({key=uid}):call('.msg_handler','update_player_control_value',uid,win_coins,self_table_type)
                    print("update_player_control_value===111",ok,coins)
                    gm_store_coins_add = (ok and coins) and coins or 0
                end

                if system_result_type == constant.PERSONAL_STORE_LOSE and win_coins < 0 then
                    local ok,coins = R().basesvr({key=uid}):call('.msg_handler','update_player_control_value',uid,win_coins,self_table_type)
                    print("update_player_control_value===222",ok,coins)
                    gm_store_coins_add = (ok and coins) and -coins or 0
                end
            end
        end
        total_num = total_num + 1
    end

    change_store_coins = change_store_coins - gm_store_coins_add
    change_robot_store(-change_store_coins) --增减系统库存 减掉GM库存部分

    local str_date = os.date("%Y%m%d%H%M%S")
    local side_list = {}
    local all_cards = curr_kpqz_instance:get_all_player_tile_list()
    dbglog("all_cards",cards.card_list_to_str(all_cards))
    for uid,info in pairs(all_cards) do
        local side_info = {}
        side_info.uid = uid
        side_info.side = info.side
        side_info.cards = info.cards

        local uid_bet_result = bet_result_map[uid] or {}
        local card_times = (add_score_map[uid] or 0) >= 0 and (uid_bet_result.card_times or 0) or -(uid_bet_result.card_times or 0)
        side_info.card_type = uid_bet_result.type or 0
        side_info.is_banker = uid_bet_result.is_banker or false
        side_info.card_times =  card_times
        local banker_times,bet_times = curr_kpqz_instance:get_banker_and_bet_times(uid)
        side_info.banker_times = banker_times
        side_info.bet_times = bet_times
        side_info.add_score = add_score_map[uid] or 0

        table_insert(side_list,side_info)
    end

    local exinfo = {
        system_result = system_result_type or 0,
        control_uid = gm_control_uid or 0,
    }
    billlog({ op = "card_record",
        table_gid = str_date .. "_" .. self_table_id,
        game_type = self_game_type, 
        table_type = self_table_type,  
        begin_time = game_start_bet_time, 
        end_time = util.get_now_time(),  
        curr_round = curr_round,  
        password = self_password, 
        winner_list = winners,  
        loser_list = losers, 
        system_win = -player_win,
        side_list = side_list,
        exinfo = exinfo,
        total_num = total_num,
        address = skynet.address(skynet.self()),
    })

    local system_win_lose = 0
    local total_coins = 0
    for _,record in pairs(winners) do
        if not is_robot(record.uid) then
            R().basesvr({key=record.uid}):send('.msg_handler','add_person_water',record.uid,math_abs(record.add_score) + math_abs(pay_fee_map[record.uid] or 0),not is_robot(record.uid))
            system_win_lose = system_win_lose - math_abs(record.add_score)
            if not is_robot(record.uid) then
                billlog({ op   = "water_history",  
                    table_gid  = str_date .. "_" .. self_table_id,
                    table_type = self_table_type,
                    uid        = record.uid,
                    value      = math_abs(record.add_score) + math_abs(pay_fee_map[record.uid] or 0),
                    is_robot   = false,
                    r          = constant.WATER_TYPE.WATER_WIN
                })

            end 
            total_coins = total_coins + math_abs(record.add_score) + math_abs(pay_fee_map[record.uid] or 0)
            R().basesvr({key=record.uid}):send('.msg_handler','add_today_win_coins',record.uid,record.add_score)
            R().basesvr({key=record.uid}):send('.msg_handler','add_play_count',record.uid,1)

            local player_info = player_info_list[record.uid]

            local pay_fee = math_abs(pay_fee_map[record.uid])
            player_info.all_win_lose = (player_info.all_win_lose or 0) + record.add_score
            player_info.all_produce = (player_info.all_produce or 0) + record.add_score + pay_fee
            R().basesvr({key=record.uid}):send('.msg_handler','add_total_coins',record.uid,record.add_score)
            player_info.all_free = (player_info.all_free or 0) + pay_fee

            local uid = record.uid
            local bet_coins = 0
            local produce = record.add_score + pay_fee
            local win_lose = record.add_score
            local free = pay_fee
            expand.sendSummary(self_table_type, uid, bet_coins, produce, win_lose, free)            

        end
    end

    for _,record in pairs(losers) do
        -- local curr_coins = get_player_curr_coins(record.uid)
        if not is_robot(record.uid) then
            R().basesvr({key=record.uid}):send('.msg_handler','add_person_water',record.uid,math_abs(record.add_score),is_robot(record.uid))
            system_win_lose = system_win_lose + math_abs(record.add_score)
            if not is_robot(record.uid) then
                billlog({ op   = "water_history",  
                    table_gid  = str_date .. "_" .. self_table_id,
                    table_type = self_table_type,
                    uid        = record.uid,
                    value      = math_abs(record.add_score),
                    is_robot   = false,
                    r          = constant.WATER_TYPE.WATER_LOSE
                })
            end 
            total_coins = total_coins + math_abs(record.add_score) + math_abs(pay_fee_map[record.uid] or 0)
            R().basesvr({key=record.uid}):send('.msg_handler','add_play_count',record.uid,1)

            local player_info = player_info_list[record.uid]

            player_info.all_win_lose = (player_info.all_win_lose or 0) + record.add_score
            player_info.all_produce = (player_info.all_produce or 0) + record.add_score
            R().basesvr({key=record.uid}):send('.msg_handler','add_total_coins',record.uid,record.add_score)


            local uid = record.uid
            local bet_coins = 0
            local produce = record.add_score
            local win_lose = record.add_score
            local free = 0
            expand.sendSummary(self_table_type, uid, bet_coins, produce, win_lose, free)              

        end

        -- if not is_robot(record.uid) and curr_coins <= constant.POCHAN_COINS then --记录破产日志
        --     billlog({op = "bankrupt_register",uid = record.uid,game_type = self_game_type,
        --         table_type = self_table_type,
        --         ret = curr_coins,
        --     })
        -- end
    end
    if system_win_lose ~= 0 then
        R().friendsvr(1):send('.info_mgr', 'set_room_summary', self_table_type, {table_type = self_table_type, lose_win = system_win_lose})
    end
    return total_coins
end

local function notify_all_coins_change(results)
    local change_list = {}

    for uid, ret in pairs(results) do
        local player_info = player_info_list[uid]
        if player_info then
            table_insert(change_list, { uid = uid, coins = ret.curr })
        end
        if not is_robot(uid) and uid_game_session_map[uid] <= 0 then
         --玩家已经不再房间通知大厅金币变化
            R().hallsvr{ key = uid }:send(".msg_handler", "toagent", uid, "notify_money_changed", { coins = ret.curr }, nil)
        end
    end


    notify_all("table.NTF_MONEY_CHANGED",{ chged_list = change_list})
end

local function notify_caishenjiadao(uid,coins)
    print("=============notify_caishenjiadao",uid,coins)
    local curr_time = util.get_now_time()
    local countdown_time = global_configs.caishenjiadao.countdown_time or 10
    local end_time = curr_time + countdown_time + GAME_TIME_SHOW_AND_SETTLE
    caishen_coins_map[uid] = coins
    send_to_gateway(uid,uid_game_session_map[uid],'table.NTF_CAISHENJIADAO',{end_time = end_time,trigger_coins = coins,game_type = self_game_type})
    billlog({op = "caishenjiadao",type = "trigger",uid = uid,table_type = self_table_type,coins = coins})
end

local function new_settle_game_coins(bet_result_list)
    local reduce_tasks = {}
    local add_tasks    = {}
    local banker_win_coins  = 0
    local banker_lost_coins = 0
    local results = {}

    --print("new_settle_game_coins33333",tostring_r(bet_result_list))

    for uid, uid_bet_result in pairs(bet_result_list) do
        local result_coins = uid_bet_result.result_coins
        if not curr_kpqz_instance:is_banker(uid) then
            local robot = is_robot(uid)
            if robot then
                local player_info = player_info_list[uid]
                player_info.coins = player_info.coins + result_coins
                results[uid] = {curr = player_info.coins}
                if result_coins < 0 then
                    banker_win_coins = banker_win_coins + math_abs(result_coins)
                else
                    banker_lost_coins = banker_lost_coins + result_coins
                end              
            else
                if result_coins < 0 then
                    table_insert(reduce_tasks,{ f = reduce_coins,id = uid,params = { uid,math_abs(result_coins),reason.LOSE_COIN,{is_robot=robot},get_extra_bill_info(uid)} })
                else
                    table_insert(add_tasks,{ f = add_coins,id = uid,params = { uid,math_abs(result_coins),reason.WIN_COIN,{is_robot=robot},get_extra_bill_info(uid)} })
                end
            end
        end
    end

    local ok, add_results = cocall(COCALL_TIMEOUT, add_tasks)
    if not ok then
        errlog(string_format("failed to cocall add tasks results(%s)", tostring_r(add_results)))
        return false
    end

    local ok, reduce_results = cocall(COCALL_TIMEOUT, reduce_tasks)
    if not ok then
        errlog(string_format("failed to cocall reduce tasks results(%s)", tostring_r(reduce_results)))
        return false
    end

    for uid, ret in pairs(reduce_results) do
        banker_win_coins = banker_win_coins + ret.chged
    end
    for uid, ret in pairs(add_results) do
        banker_lost_coins = banker_lost_coins + ret.chged
    end
    local banker_add_coins = banker_win_coins - banker_lost_coins

    local banker_info = assert(curr_kpqz_instance:get_banker_info()) 
    local banker_uid  = banker_info.banker_uid
    local tasks = {}
    local robot = is_robot(banker_uid)
    if is_robot(banker_uid) then
        local player_info = player_info_list[banker_uid]
        player_info.coins = player_info.coins + banker_add_coins
        results[banker_uid] = {curr = player_info.coins}
    else
        if banker_add_coins >= 0 then
            table_insert(tasks,{ f = add_coins,id = banker_uid,params = { banker_uid,math_abs(banker_add_coins),reason.WIN_COIN,{is_robot=robot},get_extra_bill_info(banker_uid)} })
        else
            table_insert(tasks,{ f = reduce_coins,id = banker_uid,params = { banker_uid,math_abs(banker_add_coins),reason.LOSE_COIN,{is_robot=robot},get_extra_bill_info(banker_uid)} })
        end
    end
    local ok, banker_results = cocall(COCALL_TIMEOUT, tasks)
    if not ok then
        errlog(string_format("failed to cocall banker tasks results(%s)", tostring_r(banker_results)))
        return false
    end

    --修正实际加减多少钱
    for uid,result in pairs(add_results) do
        results[uid] = result
        bet_result_list[uid].result_coins = result.chged
    end
    for uid,result in pairs(reduce_results) do
        results[uid] = result
        bet_result_list[uid].result_coins = -result.chged
    end
    for uid,result in pairs(banker_results) do
        results[uid] = result
        if banker_add_coins >= 0 then
            bet_result_list[uid].result_coins = result.chged
        else
            bet_result_list[uid].result_coins = -result.chged
        end
    end

    notify_all_coins_change(results)
   -- dbglog(string_format("new_settle_game_coins results(%s)", tostring_r(results)))

    return true
end

local function settle_event_trigger(bet_result_map)
    for uid, uid_bet_result in pairs(bet_result_map) do
        if system_do_banker(uid) then
            goto continue
        end

        if is_robot(uid) then
            goto continue
        end

        -- events.on_winning({ 
        --     uid = uid, 
        --     game_type = self_game_type, 
        --     table_type = self_table_type, 
        --     coins = uid_bet_result.result_coins,
        --  })

        -- --触发赢钱事件
        -- if uid_bet_result.result_coins > 0 then
        --     R().exdbsvr(1):send(".event_mgr", "trigger", "on_winning", { 
        --         uid = uid, 
        --         game_type = self_game_type, 
        --         table_type = self_table_type, 
        --         coins = uid_bet_result.result_coins,
        --      })
        -- end
        -- events.on_play({ 
        --     uid = uid, 
        --     game_type = self_game_type, 
        --     table_type = self_table_type 
        -- })

        -- R().exdbsvr(1):send(".event_mgr", "trigger", "on_play", { 
        --     uid = uid, 
        --     game_type = self_game_type, 
        --     table_type = self_table_type 
        -- })

        ::continue::
    end
end

local function send_gameover_msg(bet_result_map, system_banker_result)
    local rsp_bet_result_list = {}

    for uid, uid_bet_result in pairs(bet_result_map) do
        table_insert(rsp_bet_result_list, uid_bet_result)
    end

    if system_banker_result then
        table_insert(rsp_bet_result_list, system_banker_result)
    end
    
     notify_all("kpqz.NTF_GAME_RESULT", { 
        bet_result_list = rsp_bet_result_list, 
        left_time = GAME_TIME_SHOW_AND_SETTLE,
        player_info_list = get_rsp_seat_player_info_list()
     })

    game_bet_result_list = rsp_bet_result_list
end

local function check_two_round_no_action(action_uid)
    dbglog(string_format("3----------------->two_round_action_record_map(%s) action_uid(%d)", tostring_r(two_round_action_record_map), action_uid))
    local round1_action_record_map = two_round_action_record_map[1]
    local round2_action_record_map = two_round_action_record_map[2]

    if not round2_action_record_map then
        return false
    end

    local round1_record = round1_action_record_map[action_uid]
    local round2_record = round2_action_record_map[action_uid]

    dbglog(string_format("4----------------round1_record(%s) round2_record(%s)", tostring_r(round1_record), tostring_r(round2_record)))

    if not round2_record then
        return false
    end


	if not round2_record[kpqz.BET_ACTION] and not round2_record[kpqz.CACL_CATTLE_ACTION] and not round2_record[kpqz.GRAB_BANKER_ACTION] and
		not round1_record[kpqz.BET_ACTION] and not round1_record[kpqz.CACL_CATTLE_ACTION] and not round1_record[kpqz.GRAB_BANKER_ACTION]
	then
		return true
	end
	
	--[[
    if not round2_record[kpqz.BET_ACTION] and not round2_record[kpqz.CACL_CATTLE_ACTION] and not round2_record[kpqz.GRAB_BANKER_ACTION] then
        return true
    end

    if not round1_record[kpqz.BET_ACTION] and not round1_record[kpqz.CACL_CATTLE_ACTION] and not round1_record[kpqz.GRAB_BANKER_ACTION] then
        return true
    end
    --]]


    return false
end

local function when_not_action_standup()
    for _, uid_bet_result in pairs(game_bet_result_list) do
        local uid = uid_bet_result.uid

        if check_two_round_no_action(uid) then
            deal_stand_up(uid, STANDUP_NOT_OPERATE)
        end
    end    
end

local function kick_coins_not_enough_player()
    for position, uid in pairs(seated_position_uid_map) do
        if player_info_list[uid] then
            local player_info = player_info_list[uid]

            local ok, ret = check_sit_down_coins(uid, player_info.coins)
            if not ok then
                local reason = STANDUP_NOT_ENOUGH_COINS
                if ret == error_code.GOLD_OVER_LIMIT then
                    reason = STANDUP_OVER_COINS
                end
                if not is_robot(uid) then
                    deal_stand_up(uid, reason)
                else
                    deal_robot_leave(uid)
                end
            end
        end
    end
end

local function get_lose_coins(uid)
    local ok,ret = R().basesvr{key=uid}:call('.msg_handler','get_lose_coins',uid)
    if not ok then
        errlog(uid,'failed to get_lose_coins ok is nil',ok)
        return
    end

    return ret
end

local function update_two_round_action_record_map(action_record_map)
 --   dbglog(string_format("1----------------->two_round_action_record_map(%s)", tostring_r(two_round_action_record_map)))
    if #two_round_action_record_map == 2 then
        table_remove(two_round_action_record_map)
    end

    table_insert(two_round_action_record_map, 1, action_record_map)
  --  dbglog(string_format("2----------------->two_round_action_record_map(%s)", tostring_r(two_round_action_record_map)))
end

local function update_robot_store(total_coins)
   local roomdata = global_configs.roomdata[self_table_type]
   if not roomdata or not roomdata.open_robot then
        return
   end

   local cnf = global_configs.robot_store[self_table_type]
   if not cnf then
        return
   end

 --  print("update_robot_storebbbbbbbbbb",total_coins,system_send_award)
   if system_send_award then
        return
   end

   local fee_rate    = (cnf.fee_rate or 5) / 10000
   local award_rate  = (cnf.award_rate or 5) / 10000
   local fee_coins   = fee_rate * total_coins
   local award_coins = award_rate * total_coins
   
   R().exdbsvr(1):send('.system_coins_mgr','update_store',self_game_type,self_table_type,fee_coins,award_coins)
end

local function check_store_transfer()
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata or not roomdata.open_robot then
        return
    end
 
    local cnf = global_configs.robot_store[self_table_type]
    if not cnf then
         return
    end

    local base_min  = cnf.base_min or 0
    local base_max  = cnf.base_max or 0
    local base_normal = cnf.base_normal or 0
    local data = {base_min = base_min,base_max = base_max,base_normal = base_normal,table_type = self_table_type}
    R().exdbsvr(1):send('.system_coins_mgr','check_store_transfer',self_game_type,data)
 end

local function check_today_coins_water_goal(uid,data)
    local seven_reward = global_configs.seven_reward
    local goal_list = {}
    for k,v in pairs(seven_reward) do  
        table_insert(goal_list,(v.coins_low + v.coins_hight)/2)
        table_insert(goal_list,coins_hight)
    end

    local function get_section_and_high(value)
        local section,section_high
        for num,data in pairs(seven_reward) do
            if value >= data.coins_low then
                section = num
                section_high = data.coins_hight
            end 
        end

        return section,section_high
    end
    
    for _,value in pairs(goal_list) do
        if (data.pre_coins_water or 0) < value and (data.curr_coins_water or 0) >= value then
            local section,section_high = get_section_and_high(data.curr_coins_water or 0)
            local ntf = {
                today_coins   = data.curr_coins_water or 0,
                section_count = section,
                section_hight = section_high or 0,
                section_total_count = #seven_reward,
            }
            send_to_gateway(uid,uid_game_session_map[uid],'hall.NTF_SEVEN_REWARD_GOAL',ntf)
        end
    end
end

local function add_player_coins_water(bet_result_map)
    local player_win = 0
    --print("add_player_coins_water",tostring_r(bet_result_map))
    for uid, uid_result_map in pairs(bet_result_map) do
        if not is_robot(uid) then
            local ok,ret = R().basesvr{key = uid}:call('.msg_handler','add_player_coins_water',uid,math_abs(uid_result_map.result_coins))
            -- if ok and ret then
            --     check_today_coins_water_goal(uid,ret)
            -- end
        end
    end
end

local function send_real_horse_message(bet_result_list)
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local exinfo = cjson.encode({game_type = self_game_type})
    for uid, uid_bet_result in pairs(bet_result_list) do
      --  print("===========1111111111",uid_bet_result.result_coins,target_coins)
        if uid_bet_result.result_coins > 0 then
            local player_info = player_info_list[uid]
            if player_info then
                R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',constant.BROADCAST_IDS.BIDS_KPQZ,
                {name = player_info.name,coin = uid_bet_result.result_coins,vip_level = player_info.vip_level})

                R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',constant.BROADCAST_IDS.GUIDE_BROAD,
                {vip_level = player_info.vip_level,name = player_info.name,game_name = roomdata.name or '',coin =  uid_bet_result.result_coins,exinfo = exinfo})
            end
        end
    end
end

function robot_use_pic(bet_result_map,system_banker_result)
    local bet_result_list = {}

    for uid, uid_bet_result in pairs(bet_result_map) do
        table_insert(bet_result_list, uid_bet_result)
    end
    if system_banker_result then
        table_insert(bet_result_list, system_banker_result)
    end

    for _, uid_bet_result in pairs(bet_result_list) do
        if uid_bet_result.result_coins > 0 then
            if uid_bet_result.type == 1 then      
                trigger_event('on_use_picture',uid_bet_result.uid,global_configs.robot_picture.kpqz.one_win) 
            elseif uid_bet_result.type == 10 then --牛牛胜利
                trigger_event('on_use_picture',uid_bet_result.uid,global_configs.robot_picture.kpqz.ten_win)
            elseif uid_bet_result.type == 0 then  --高牌赢钱
                trigger_event('on_use_picture',uid_bet_result.uid,global_configs.robot_picture.kpqz.nomal_win)
            end
        else
            if uid_bet_result.type > 7 then   --牛七以上输钱
                trigger_event('on_use_picture',uid_bet_result.uid,global_configs.robot_picture.kpqz.seven_lose)
            end
        end
    end
end

local function audit_game_result(game_result)
    local bet_result_map = game_result.bet_result_map
    local game_valid = game_result.game_valid
    --local pay_fee_map = game_result.pay_fee_map
    local action_record_map = game_result.action_record_map
    local system_banker_result = nil

    dbglog(string_format("game_result(%s)", tostring_r(game_result)))

    if bet_result_map[constant.SYSTEM_DO_BANKER_UID] then
        system_banker_result = bet_result_map[constant.SYSTEM_DO_BANKER_UID]
        bet_result_map[constant.SYSTEM_DO_BANKER_UID] = nil
    end

    if not game_valid then
        errlog("game_valid is false, no need to settle")
        send_gameover_msg(bet_result_map, nil)
        return false
    end

    local ok, results = new_settle_game_coins(bet_result_map)
    if not ok then
        return false
    end
    
    local roomdata = global_configs.roomdata[self_table_type]
    local fee_percent = roomdata and (roomdata.cost / 100)  or 0.05
    
    --计算系统输赢
    local player_win = 0
    for uid, uid_bet_result in pairs(bet_result_map) do
         if not is_robot(uid) then
            player_win = player_win + uid_bet_result.result_coins
        end
    end
    local system_money_cost = -player_win

    print("===============control_uid",control_uid,system_money_cost)
    -- gm_store_coins_add = 0
    -- if control_uid then
    --     local change_count = 0
    --     local uid_bet_result = game_result.bet_result_map
    --     local pattern_result = uid_bet_result[control_uid] or {result_coins = 0}
    --     change_count =  pattern_result.result_coins

    --     if control_result == CONTROL_RESULT_WIN and change_count > 0 then
    --         R().basesvr({key=control_uid}):send('.msg_handler','update_player_control_value',control_uid,change_count,self_table_type)
    --         if is_curr_have_robot and system_money_cost < 0 then
    --             local gm_add_coins = math_abs(system_money_cost) < math_abs(change_count) and math_abs(system_money_cost) or math_abs(change_count)
    --             --R().exdbsvr(1):send('.gm_coins_mgr','reduce_gm_coins',gm_add_coins,{uid=control_uid,table_type=self_table_type}) --减GM库存
    --             gm_store_coins_add = -gm_add_coins
    --         end
    --     elseif control_result == CONTROL_RESULT_LOSE and change_count < 0 then
    --         R().basesvr({key=control_uid}):send('.msg_handler','update_player_control_value',control_uid,math_abs(change_count),self_table_type)
    --         if is_curr_have_robot and system_money_cost > 0 then
    --             local gm_add_coins = math_abs(system_money_cost) < math_abs(change_count) and math_abs(system_money_cost) or math_abs(change_count)
    --             --R().exdbsvr(1):send('.gm_coins_mgr','add_gm_coins',gm_add_coins,false,{uid=control_uid,table_type=self_table_type}) --加GM库存
    --             gm_store_coins_add = gm_add_coins
    --         end
    --     end
    -- end

    --添加玩家金币流水
    add_player_coins_water(game_result.bet_result_map)

	--扣台费
	local deduct_pay_deal = {}
    local pay_fee_map = {}

	for uid, uid_result_map in pairs(game_result.bet_result_map) do
        if uid_result_map.result_coins > 0 then
           -- dbglog("==================111111 > 0")
            R().basesvr({key=uid}):send('.msg_handler','add_wining_times',uid,self_game_type)
			local fee = math_floor(uid_result_map.result_coins * fee_percent)
			pay_fee_map[uid] = fee

            if is_robot(uid) then
                local player_info = player_info_list[uid]
                player_info.coins = player_info.coins - fee
                game_result.bet_result_map[uid].result_coins = game_result.bet_result_map[uid].result_coins - fee
            else
                table_insert(deduct_pay_deal, { 
                    f = reduce_coins, 
                    id = uid,
                    params = { uid, fee, reason.PAY_FEE, { is_robot = is_robot(uid) }, get_extra_bill_info(uid)} })
                 --检查是否触发财神驾到
                 local trgger_coins = uid_result_map.result_coins - fee
                 local player_info = player_info_list[uid]
                 if player_info and not is_robot(uid) then
                     local vip_level = player_info.vip_level
                     if system_store.check_trigger_caishenjiadao(uid,trgger_coins,self_table_type,vip_level or 0) then
                         notify_caishenjiadao(uid,trgger_coins)
                     end
                 end 
             end
            --添加玩家首冲进度
            R().activitysvr(1):send('.activity_mgr','act_add_first_charge_process',uid,uid_result_map.result_coins)
        elseif uid_result_map.result_coins <= 0 then
    --        dbglog("==================111111 < 0")
            R().basesvr({key=uid}):send('.msg_handler','add_losing_times',uid,self_game_type)
			pay_fee_map[uid] = 0
		end
	end
	
	local ok, co_results = cocall(COCALL_TIMEOUT, deduct_pay_deal)
	if not ok then
		errlog(string_format("failed to cocall co_results(%s)", tostring_r(co_results)))
	end

    if co_results and next(co_results) then
        for uid,record in pairs(co_results) do
            if game_result.bet_result_map[uid] then
                game_result.bet_result_map[uid].result_coins = game_result.bet_result_map[uid].result_coins - record.chged
            end
        end
    end
    
    send_gameover_msg(bet_result_map, system_banker_result)    
    settle_event_trigger(bet_result_map)
    local total_coins = write_card_record_log(bet_result_map, pay_fee_map)
    total_coins = total_coins - math_abs(gm_store_coins_add)
    update_robot_store(total_coins)
    check_store_transfer() --检测库存转移

    update_two_round_action_record_map(action_record_map)
    send_real_horse_message(bet_result_map)
 
    --牛七以上输钱,牛牛胜利,牛一赢钱,高牌赢钱 机器人触发表情
    robot_use_pic(bet_result_map,system_banker_result) 

    return true
end

local function have_player()
    for uid,_ in pairs(player_info_list) do
        if not is_robot(uid) then
            return true
        end
    end
    return false
end

local function put_back_robots()
    local robot_ids_list = {}
    for uid, _ in pairs(player_info_list) do
        if is_robot(uid) and robot_manager[uid] then
            robot_manager[uid]['on_exit'](robot_manager[uid])
            -- if enter_robot_map[uid] then
            --     enter_robot_map[uid] = nil 
            -- end
        end
    end
end

local function unlock_all_players()
    for uid, _ in pairs(uid_game_session_map) do
        unlock_one_player(uid)
    end
end

local function dissmiss_table()
    for uid, fd in pairs(uid_game_session_map) do
        if fd > 0 then
            send_to_gateway(uid,fd,'kpqz.RSP_LEAVE',{result=error_code.RSP_SUCESS})
            watch_session(fd, uid, false)
        end

        skynet.send('.table_mgr','lua','leave',uid)
    end

    --玩家解除桌子锁定
    unlock_all_players()
    put_back_robots()
    billlog({ op = "close_table", status = curr_status, password = self_password })
    return true
end

local function check_stop_table()
    if stopping_server then        
        billlog({op = "stop_table",status = curr_status,password = self_password})
        closing_server = true --直接关闭桌子
        return true
    end

    return false
end

local function check_close_table(curr_time)
    local closable = false
    if closing_server then
        closable = true
    end
    if not closable then
        if curr_time - game_start_init_time <= 30 then
            return
        end
        if not have_player() and not curr_kpqz_instance then
            closable = true
        end
    end
    if not closable then
        return
    end
    if is_on_match() and not closing_server then
        print("table is on matching",closable)
        return
    end

    local ok, ret = xpcall(dissmiss_table, debug.traceback)
    if not ok then
        errlog(ret)
    end

    local uids = {}
    for uid,  _ in pairs(player_info_list) do
        table_insert(uids, uid)
    end

    notify_matchsvr_table_stats(true)
    skynet.send(".table_mgr", "lua", "on_table_delete", self_table_id, uids)
    dbglog("========11111111now delete this table", self_table_id, uids)
    skynet.exit()
end

local function get_first_four_cards(side_cards_map, side)
    local tmp_cards_list = side_cards_map[side]
    local self_card_list = {}

    for i = 1, 4 do 
        table_insert(self_card_list, tmp_cards_list[i])
    end

    return self_card_list
end

local function get_first_three_cards(side_cards_map, side)
    local tmp_cards_list = side_cards_map[side]
    local self_card_list = {}

    for i = 1, 3 do 
        table_insert(self_card_list, tmp_cards_list[i])
    end

    return self_card_list
end

local function get_grab_banker_max_times(uid)
    local max_times = 0
    local player_info = player_info_list[uid]
    if is_robot(uid) then
        local bet_num = get_seated_num() - 1
        local max_bet_times = sorted_can_bet_times_list[#sorted_can_bet_times_list]
        local max_card_times = MAX_CARD_TIMES
        local dizhu = self_game_config.dizhu
        local value = bet_num * max_bet_times * max_card_times * dizhu
        if value == 0 then
            max_times = grab_banker_times_dizhu_map[#grab_banker_times_dizhu_map]
        else
            max_times = math_floor(player_info.coins * 4 / value)
        end
    else
   --     print("get_grab_banker_max_times========111",uid,tostring_r(grab_banker_times_dizhu_map))
        for i = #grab_banker_times_dizhu_map, 1, -1 do
            local sub_config = grab_banker_times_dizhu_map[i]
            local min, max = sub_config[1], sub_config[2]
 --           print("get_grab_banker_max_times========222",min,max,player_info.coins,self_game_config.dizhu)
            if not min then
                max_times = i
                break
            end

            if player_info.coins >= min * self_game_config.dizhu then
                max_times = i
                break
            end
        end
    end

    return max_times
end

local function send_ntf_start_msg()
    local side_cards_map = curr_kpqz_instance:get_side_cards_map()
    local player_data_map = curr_kpqz_instance:get_player_data_map()
    local rsp_player_info_list = get_rsp_seat_player_info_list()    

    for uid, player_data in pairs(player_data_map) do
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_START
        msg.grab_banker_max_times = get_grab_banker_max_times(uid)
        msg.self_card_list = get_first_three_cards(side_cards_map, player_data.side)
        msg.player_info_list = rsp_player_info_list
        if not is_robot(uid) then
            send_to_gateway(uid, game_session, "kpqz.NTF_START", msg)
        else
      --      print("on start------------",msg.grab_banker_max_times)
            robot_manager[uid]['on_start'](robot_manager[uid],curr_kpqz_instance,msg.grab_banker_max_times)
        end
    end

--    dbglog(string_format("------------noseat_players_list(%s)", tostring_r(noseat_players_list)))

    for _, uid in pairs(noseat_players_list) do
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_START
        msg.grab_banker_max_times = 0
        msg.self_card_list = {}
        msg.player_info_list = rsp_player_info_list

        send_to_gateway(uid, game_session, "kpqz.NTF_START", msg)
    end
end

local function return_coins(results, pay_coins)
    local return_op_deal = {}
    for uid, ret in pairs(results) do
        if ret then
            table_insert(return_op_deal, { f = add_coins, id = uid, params = { 
                uid, pay_coins, reason.COST_COIN_ERROR_BACK, { is_robot = is_robot(uid) }, get_extra_bill_info(uid) } })  
        end
    end
    
    local ok, co_results = cocall(COCALL_TIMEOUT, return_op_deal)
    if not ok then
        errlog(string_format("failed to cocall co_results(%s)", tostring_r(co_results)))
        return false
    end   

    return true
end

local function get_pay_result_detail(co_results, pay_ok_map)
    local pay_fail_list = {}

    for uid, ret in pairs(co_results) do
        if not ret then
            table_insert(pay_fail_list, uid)
        else
            pay_ok_map[uid] = ret
        end
    end

    for i, uid in pairs(pay_fail_list) do
        deal_stand_up(uid, STANDUP_NOT_ENOUGH_COINS)
    end

    if not player_num_meet_condition() then
        return false
    end

    return true
end

local function change_player_status_to_playing(pay_ok_map)
    for uid, _ in pairs(pay_ok_map) do
        local player_info = player_info_list[uid]
        if player_info then
            player_info.status = PLAYER_STATUS_PLAYING
            curr_kpqz_instance:init_player_data(uid,player_info.position,player_info.coins)
        end
    end
end

local function get_total_coins(robot_type)
    local ok,succ,total_coins = R().robotmgr(1):call('.msg_handler','get_total_coins',robot_type)
    if not ok then
        errlog("get_total_coins fail because ok is nil")
        return
    end
    
    return succ,total_coins
end

get_random_robot_uid = function()
    local robot_ids = {}
    for _,uid in pairs(seated_position_uid_map) do
        if is_robot(uid) then
           table_insert(robot_ids,uid) 
        end
    end
    if #robot_ids > 0 then
        local random_index = math_random(1,#robot_ids)
        return robot_ids[random_index]
    end
    return nil
end

--控制模式下玩家是否拿最大的牌，或最小的牌
local function get_pattern_best_cards()
    local will_get_best_cards
    local special_uid
    local rand = math_random(1, 100)

    if affect_rate > 0 and rand <= affect_rate then
        special_uid = pattern_player_uid
        will_get_best_cards = true
        dbglog("===========1111111get best cards",will_get_best_cards)
    elseif affect_rate < 0 and rand <= math.abs( affect_rate ) then
        special_uid = pattern_player_uid
        will_get_best_cards = false
        dbglog("===========1111111not best cards",will_get_best_cards)
    end

    dbglog("===========1111111",special_uid,will_get_best_cards)
    return special_uid, will_get_best_cards
end

local function get_robot_best_cards()
    local will_get_best_cards
    local special_uid
    local roomdata = global_configs.roomdata[self_table_type]
    if roomdata.open_robot and roomdata.robot_type then
        local robot_config = global_configs.robot[roomdata.robot_type]
        local ok, total_coins = get_total_coins(roomdata.robot_type)
        if robot_config and ok then
            for _, config in pairs(robot_config.probability_config) do
                local lower_bound, upper_bound, prob_modifier = table_unpack(config)
                if total_coins >= lower_bound and total_coins <= upper_bound then
                    local rand = math_random(1, 100)
                    if rand <= math_abs(prob_modifier) then
                        will_get_best_cards = prob_modifier > 0
                        special_uid = get_random_robot_uid() -- todo
                    end
                    break
                end
            end
        end
    end

    return special_uid,will_get_best_cards
end


local function start_kpqz_instance()
    local cost = self_game_config.cost
    local dizhu = self_game_config.dizhu

    local instance = kpqz:new()
  --  print("999999999999999999999999999999->",instance)
    instance:init(max_seat_num, card_type_times_map, sorted_can_bet_times_list, cost, dizhu, self_game_type)

    curr_kpqz_instance = instance
end

local function meet_start_condition()
    --1.判断座位上的玩家数量是否超过2个
    if not player_num_meet_condition() then
        return false
    end

	-- 改为赢家抽水，所以所有在座的人都在pay_ok_map
	local pay_ok_map = {}
	
	for position, uid in pairs(seated_position_uid_map) do
		if type(uid) == "number" then
			pay_ok_map[uid] = 0
		end
	end
	

    start_kpqz_instance()
    change_player_status_to_playing(pay_ok_map)

    local special_uid,will_get_best_cards
    
    --if self_table_pattern ~= TABLE_PATTERN_CONTROLLD then 
    --    special_uid,will_get_best_cards = get_robot_best_cards()
    --end

    --[[if self_table_pattern == TABLE_PATTERN_CONTROLLD and affect_count ~= 0 and affect_rate ~= 0 then
        if curr_kpqz_instance:check_player_playing(pattern_player_uid) then
            local pattern_uid, is_get_best_cards = get_pattern_best_cards()
            special_uid = pattern_uid
            will_get_best_cards = is_get_best_cards
        end
    end]]

    curr_kpqz_instance:deal(special_uid,will_get_best_cards,self_table_pattern)

    return true
end

local function time_to_start(curr_time)
    if curr_time > game_start_wait_start_time + GAME_TIME_WAIT_START then
        return true
    end

    return false
end

local function time_to_bet(curr_time)
    if curr_time > game_start_start_time + GAME_TIME_START then
        return true
    end

    return false
end

local function get_player_can_push(action_uid, banker_uid)
	return false
end

local function assembly_ntf_grab_banker_result(uid, same_max_times_list)
    local banker_info = curr_kpqz_instance:get_banker_info()
    local msg = {}
    local can_push = get_player_can_push(uid, banker_info.banker_uid)

    msg.banker_uid = banker_info.banker_uid
    msg.left_time = GAME_TIME_BET
    msg.banker_times = banker_info.banker_times
    msg.same_max_times_list = same_max_times_list
    msg.can_push = can_push

    return msg
end

local function send_ntf_grab_banker_result_msg(same_max_times_list)
	local player_data_map = curr_kpqz_instance:get_player_data_map()

    for _,player_data in pairs(player_info_list) do
        local uid = player_data.uid
        local msg = assembly_ntf_grab_banker_result(uid, same_max_times_list)
        local game_session = uid_game_session_map[uid]
        send_to_gateway(uid, game_session, "kpqz.NTF_GRAB_BANKER_RESULT", msg)
--        dbglog(uid,tostring_r(msg))
    end
end

local function time_to_cacl_cattle(curr_time)
    if curr_time > game_start_bet_time + GAME_TIME_BET then
        return true
    end

    return false
end

local function get_fifth_card(side_cards_map, position)
    local cards = side_cards_map[position]
    return cards[constant.NN_MAX_CARDS_COUNT]
end

local function get_four_five_cards(side_cards_map, side)
    local tmp_cards_list = side_cards_map[side]
    local self_card_list = {}

    for i = 4, 5 do 
        table_insert(self_card_list, tmp_cards_list[i])
    end

    return self_card_list
end

local function send_fifth_card_msg()
    local player_data_map = curr_kpqz_instance:get_player_data_map()
    local side_cards_map = curr_kpqz_instance:get_side_cards_map()

    for uid, player_data in pairs(player_data_map) do
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_CACL_CATTLE
        msg.card_list = get_four_five_cards(side_cards_map, player_data.side)
   --     print("NTF_FIFTH_CARD",tostring_r(msg))
        send_to_gateway(uid, game_session, "kpqz.NTF_FIFTH_CARD", msg)
    end

    for _, uid in pairs(noseat_players_list) do 
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_CACL_CATTLE
        msg.card_list = {}
        send_to_gateway(uid, game_session, "kpqz.NTF_FIFTH_CARD", msg)
    end
end

local function time_to_show_and_settle(curr_time)
    if curr_time > game_start_calc_cattle_time + GAME_TIME_CACL_CATTLE then
        return true
    end

    return false
end

local function time_to_wait_start(curr_time)
    if curr_time > game_start_show_and_settle_time + GAME_TIME_SHOW_AND_SETTLE then
        return true
    end

    return false
end

local function reset_game_data()
    curr_kpqz_instance = nil
    game_bet_result_list = {}
end

local function deal_bet_logic(uid, bet_times, action_type)
    local ok, ret = curr_kpqz_instance:bet_action(uid, bet_times, action_type)
    if not ok then
        errlog(string_format("uid(%d) failed to bet reason(%d)", uid, ret))
        return false, ret
    end

    notify_all("kpqz.NTF_BET", { uid = uid, bet_times = bet_times })
    return true
end

local function send_timeout_ntf_cacl_cattle_msg()
    local player_data_map = curr_kpqz_instance:get_player_data_map()

    for uid, player_data in pairs(player_data_map) do
        if not player_data.calc_end then
            notify_all("kpqz.NTF_CACL_CATTLE", { uid = uid })
            player_data.calc_end = true
        end
    end
end
--[[
local function call_can_take_compensation(uid)
    local ok, given, ret = R().basesvr({ key = uid }):call(".msg_handler", "can_take_compensation", uid)
    if not ok then
        errlog(string_format("failed to call basesvr can_take_compensation uid(%d)", uid))
        return false
    end

    if not given then
        dbglog(string_format("give is nil uid(%d)", uid))
        return false
    end

    return {
        left_times = ret,
        compensation_coins = global_configs.value.BASE_COMPENSATION_COINS
    }
end

local function can_take_compensation()
    local tasks = {}

    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            if not is_robot(uid) then
                table_insert(tasks, { f = call_can_take_compensation, id = uid, params = { uid } })
            end
        end
    end
    
    local ok, results = cocall(COCALL_TIMEOUT, tasks)
    if not ok then
        errlog(string_format("failed to cocall results(%s)", tostring_r(results)))
        return false
    end

    for uid, ret in pairs(results) do
        if ret then
            local rsp = {
                left_compensation_times = ret.left_times,
                compensation_coins = ret.compensation_coins,
            }

            send_to_gateway(uid, uid_game_session_map[uid], "hall.NTF_COMPENSATION", rsp)

            billlog({ 
                op = "bankrupt_register", 
                uid = uid, 
                game_type = self_game_type, 
                table_type = self_table_type, 
                ret = reason.BANKRUPT_REGISTER,
            })
        end
    end

    return true
end

local function notify_take_compensation(uid)
    local ret = call_can_take_compensation(uid)
    if not ret then
        return false
    end

    local rsp = {
        left_compensation_times = ret.left_times,
        compensation_coins = ret.compensation_coins,
    }

    send_to_gateway(uid, uid_game_session_map[uid], "hall.NTF_COMPENSATION", rsp)
end
--]]

local function have_not_robot_player()
    for uid, _ in pairs(player_info_list) do
        if not is_robot(uid) then
            return true
        end
    end

    return false
end

local function run_robots(new_robot_list)
    for _, robot_obj in pairs(new_robot_list) do
        skynet.fork(function() 
            robot_obj:update() 

            local uid = robot_obj.uid
            dbglog("robot exit", uid)
            robot_manager[uid] = nil
        end)
    end
end

local function robot_info_init_logic(robot_info, curr_time,robot_type)        
    local position = get_next_can_sit_position()
    local robot_uid = robot_info.uid
   -- dbglog(string_format("load robot uid(%d) position(%d)", robot_uid, position))

    local player_info = {
        uid = robot_uid,
        name = robot_info.name,
        coins = robot_info.coins,
        sex = robot_info.sex,
        icon = robot_info.icon,
        last_chat_time = 0,
        last_ping_time = curr_time,
        type = robot_type,
        icon_border = robot_info.icon_border
    }

    uid_game_session_map[robot_uid] = ROBOT_CLIENT_FD
    seated_position_uid_map[position] = robot_uid
    player_info_list[robot_uid] = player_info
    add_to_noseat(robot_uid)
    deal_sit_down(robot_uid, position)

    local roomdata = global_configs.roomdata[self_table_type]
    local robot_config = global_configs.robot[roomdata.robot_type]

    local robot_obj = nn_robot.new(robot_uid, player_info, skynet.self(), ROBOT_CLIENT_FD,self_game_type,robot_config)
    robot_manager[robot_uid] = robot_obj
    trigger_event("on_register", robot_uid)
    return robot_obj
end

local function load_robot()
    --判断是否开放机器人
    if not self_game_config.open_robot then
        dbglog("cant open robot")
        return false
    end

    --随机拉取1个机器人
    local robot_type = self_game_config.robot_type
    local robotdata = global_configs.robot[robot_type]
    if not robotdata then
        return false
    end

    local coins_limit = {robotdata.min_coin,robotdata.max_coin}
    local add_robot_num = curr_round_need_player - get_seated_num()
    local left_num = table_def.game_player_num[self_game_type] - get_seated_num()
    add_robot_num = math_min(left_num,add_robot_num)
    if add_robot_num <= 0 then
        return
    end

    local ok, succ, ret_map = R().robotmgr(1):call(".msg_handler", "get_robots",add_robot_num,robot_type,coins_limit)
    if not ok then
        errlog("call robotmgr get_robot error ")
        return false
    end

    if not succ then
        errlog("call robotmgr get_robot error ", succ)
        return false
    end

    --再次检查人数是否已经满了 如果满了将机器人送回
    if get_seated_num() + #ret_map > table_def.game_player_num[self_game_type] then
        --[[
        local put_back_robot = {}
        for robot_uid, _ in pairs(ret_map) do
            table_insert(put_back_robot, robot_uid)
        end
        R().robotmgr(1):send(".msg_handler", "put_back_robot", put_back_robot)
        --]]

        R().robotmgr(1):send(".msg_handler", "put_back_robots", ret_map)
        return false
    end

    local curr_time = util.get_now_time()
    local load_ok_robot_list = {}

    for _, robot_info in pairs(ret_map) do
        local robot_obj = robot_info_init_logic(robot_info, curr_time,robot_type)
        table_insert(load_ok_robot_list, robot_obj)
    end

     --通知匹配服更新人数
    notify_matchsvr_table_stats()

    --fork robot协程
    run_robots(load_ok_robot_list)
    return true
end

local function time_to_load_robot(curr_time)
    if curr_time > game_start_init_time + GAME_TIME_LOAD_ROBOT then
        game_start_init_time = curr_time
        return true
    end    

    return false
end

local function update_players_never_start()
    for uid in pairs(uid_game_session_map) do
        if not is_robot(uid) then
--            dbglog("update_players_never_start:", self_table_id, uid)
            players_never_start[uid] = nil
        end
    end
end

local function check_robot_leave_game_over()
    --人满机器人离开一人 机器人钱不够离开 
    --上面没人离开时 机器人按概率离开
    local already_exit
    if get_seated_num() >= max_seat_num then
        local leave_uid = get_random_robot_uid()
        if leave_uid then
            deal_robot_leave(leave_uid)
            --trigger_event("on_leave",leave_uid)
            already_exit = true
        end
    end

    for _,uid in pairs(seated_position_uid_map) do
        local player_info = player_info_list[uid]
        if is_robot(uid) and player_info and player_info.coins < self_game_config.min then
           --trigger_event("on_leave",uid)
           deal_robot_leave(uid)
           already_exit = true
        end
    end

    if not already_exit then
        local roomdata = global_configs.roomdata[self_table_type]
        local robot_cnf = global_configs.robot[roomdata.robot_type or 0] or {}
        local rate = robot_cnf.exit_rate or 10
        for _,uid in pairs(seated_position_uid_map) do
            local player_info = player_info_list[uid]
            if is_robot(uid) and player_info and math_random(0,100) <= rate then
               --trigger_event("on_leave",uid)
               deal_robot_leave(uid)
               break
            end
        end
    end
end

local function check_have_robot(player_list)
    for _,uid in pairs(player_list) do
        if is_robot(uid) then
            return true
        end
    end

    return false
end

--控制机器人赢或输
local function control_robot_result(win)
    local banker_info = curr_kpqz_instance:get_banker_info()
    local bet_list = curr_kpqz_instance:get_all_bet_list()

 --   dbglog("control_robot_result===",win,is_robot(banker_info.banker_uid))
    if is_robot(banker_info.banker_uid) then
        local player_list = {}
        for _,bet_info in pairs(bet_list) do
            if not is_robot(bet_info.uid) then
                table_insert(player_list,bet_info.uid)
            end
        end
        table_insert(player_list,banker_info.banker_uid)
        curr_kpqz_instance:control_banker_result(player_list,win)
    elseif not is_robot(banker_info.banker_uid) then
        local robot_list = {}
        for _,bet_info in pairs(bet_list) do
            if is_robot(bet_info.uid) then
                table_insert(robot_list,bet_info.uid)
            end
        end
        table_insert(robot_list,banker_info.banker_uid)
        local banker_win = true
        if win then
            banker_win = false
        end
        curr_kpqz_instance:control_banker_result(robot_list,banker_win)
    end
end

local function control_player_result(uid,win)
    local banker_info = curr_kpqz_instance:get_banker_info()
    local player_list = {}
    for position,_uid in pairs(seated_position_uid_map) do
     --   print("uid is ",_uid,position)
        local player_info = player_info_list[_uid] or {}
        if player_info.status == PLAYER_STATUS_PLAYING then
            table_insert(player_list,_uid)
        end
    end

    if banker_info.banker_uid == uid then
        curr_kpqz_instance:control_banker_result(player_list,win)
    else
        curr_kpqz_instance:control_player_result(uid,win)
    end
end

local function get_personal_system_store_result()
    local robot_type = self_game_config.robot_type
    local player_list = {}
    local plyaer_no_tobot_list = {}
    for position, uid in pairs(seated_position_uid_map) do
        local player_info = player_info_list[uid] or {}
        if player_info.status == PLAYER_STATUS_PLAYING then
            if not is_robot(uid) then
                table_insert(plyaer_no_tobot_list,uid)
            end
            table_insert(player_list,uid)
        end
    end 

    local robot_store = global_configs.robot_store[self_table_type]     
    local personal_control = global_configs.personal_control
    local gm_warn = (global_configs.gm_control or {}).warn_coins or 50000000
    is_curr_have_robot = check_have_robot(player_list)
    local base_warn = robot_store.base_min or 0

    return system_store.get_personal_system_store_result(self_table_type,plyaer_no_tobot_list,gm_warn,is_curr_have_robot,base_warn)
end

local function check_not_enter_player(curr_time)
    for uid,register_time in pairs(player_register_time_map) do
        if curr_time - register_time >= 30 then
            deal_player_regular_leave(uid)
            player_register_time_map[uid] = nil
        end
    end
end

--检查系统库存
local function check_store_result()
    if not self_game_config.open_robot then
        return
    end

    local store_result,personal_uid = get_personal_system_store_result()
    system_result_type = store_result
    gm_control_uid = personal_uid

    if not store_result then
        errlog("failed to get store_result",store_result,personal_uid)
        return
    end
    system_send_award = false
    -- control_uid = personal_uid
    dbglog("kpqz_check_store_result===begin====",store_result,personal_uid)
    if store_result == constant.SYSTEM_STORE_WIN then
        control_robot_result(true)
    elseif store_result == constant.SYSTEM_STORE_LOSE then
        control_robot_result(false)
    elseif store_result == constant.AWARD_STORE_LOSE then
        control_robot_result(false) --让系统输
        system_send_award = true
    elseif store_result == constant.PERSONAL_STORE_WIN then
        control_result = CONTROL_RESULT_WIN
        control_player_result(personal_uid,true)
    elseif store_result == constant.PERSONAL_STORE_LOSE then
        control_result = CONTROL_RESULT_LOSE
        control_player_result(personal_uid,false)
    end

    return
end

function update(curr_time)
    --dbglog("---------------------------=======", TABLE_STATUS_STRING[curr_status])

    if curr_status == GAME_STATUS.GAME_STATUS_INIT then
        if curr_time > game_start_init_time + GAME_TIME_LOAD_ROBOT then
            update_curr_round_need_player()
            curr_status = GAME_STATUS.GAME_STATUS_LOAD_ROBOT
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_LOAD_ROBOT then
        load_robot()
        if player_num_meet_condition() then
            game_start_wait_start_time = curr_time  
            notify_all("kpqz.NTF_WAIT_START", { left_time = GAME_TIME_WAIT_START }) 
            curr_status = GAME_STATUS.GAME_STATUS_WAIT_START
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_WAIT_START then
        if time_to_start(curr_time) then
            curr_status = GAME_STATUS.GAME_STATUS_CHECK_START
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_CHECK_START then
        if meet_start_condition() then
            curr_round = curr_round + 1
            send_ntf_start_msg()
            game_start_start_time = curr_time            
            curr_kpqz_instance:start_grab_banker()
            --trigger_event("on_start", curr_kpqz_instance)
            trigger_event("on_time_to_grab", GAME_TIME_START)
            curr_status = GAME_STATUS.GAME_STATUS_START
        else
            curr_status = GAME_STATUS.GAME_STATUS_INIT --中途有人走了,不够人数返回最开始状态
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_START then
        if (curr_kpqz_instance:grab_banker_complete() or time_to_bet(curr_time)) then
            local banker_info, same_max_times_list = curr_kpqz_instance:determine_banker()
            send_ntf_grab_banker_result_msg(same_max_times_list)
            curr_kpqz_instance:start_bet()
            trigger_event("on_time_to_bet", GAME_TIME_BET)
            game_start_bet_time = curr_time
            curr_status = GAME_STATUS.GAME_STATUS_BET
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_BET then
        if (curr_kpqz_instance:bet_complete() or time_to_cacl_cattle(curr_time)) then
            local ret = curr_kpqz_instance:do_default_bet()           
			for k,v in pairs(ret) do
				local uid = v.uid
				local bet_times = v.bet_times
				notify_all("kpqz.NTF_BET", { uid = uid, bet_times = bet_times })
			end
            check_store_result()
            
            send_fifth_card_msg()
            curr_kpqz_instance:start_cacl_cattle()
            game_start_calc_cattle_time = curr_time            
            trigger_event("on_time_to_cacl_cattle", GAME_TIME_CACL_CATTLE)    

            curr_status = GAME_STATUS.GAME_STATUS_CACL_CATTLE
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_CACL_CATTLE then
        if curr_kpqz_instance:calc_cattle_complete() or time_to_show_and_settle(curr_time) then 
            send_timeout_ntf_cacl_cattle_msg()           
            curr_kpqz_instance:judge_result()
            
            local ok, ret = xpcall(audit_game_result, debug.traceback, curr_kpqz_instance:get_game_result())
            if not ok then
                errlog(ret)
            end

            trigger_event("on_game_over")
            game_start_show_and_settle_time = curr_time
            curr_status = GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE
        end
    elseif curr_status == GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE then
        update_players_never_start()

        if time_to_wait_start(curr_time) then
            clear_offline_players()
            --can_take_compensation()
            when_not_action_standup()        
            kick_coins_not_enough_player()    
            check_robot_leave_game_over()
            check_stop_table() --停服
            reset_game_data()    
            load_config()
            --update_curr_round_need_player()    
            curr_status = GAME_STATUS.GAME_STATUS_LOAD_ROBOT          
        end
    else
        errlog(string_format("curr_status(%d) error", curr_status))
    end
    
    check_close_table(curr_time)
    check_not_enter_player(curr_time)
end

local function game_update()
    curr_status = GAME_STATUS.GAME_STATUS_INIT

    while true do
        local curr_time = util.get_now_time()
        local ok, ret = xpcall(update, debug.traceback, curr_time)
        if not ok then 
            errlog(ret)
        end
        skynet.sleep(50)
    end
end

function internal.sync_table_stats()
    local left_num = table_def.game_player_num[self_game_type] - get_seated_num()
    if left_num < 0 
        then left_num = 0 
    end

    local table_stats = {}
    table_stats.table_gid = self_table_gid
    table_stats.left_num = left_num
    table_stats.robot_num = get_seated_robot_num()
    table_stats.human_num = get_seated_human_num()
    table_stats.players_never_start = players_never_start
    table_stats.table_pattern = self_table_pattern
    return self_table_type, table_stats
end

function internal.get_game_type()
    return self_game_type, self_table_type
end

local function get_non_robot_players_num()
    local player_num = 0

    for _, uid in pairs(seated_position_uid_map) do
        if uid then
            if not system_do_banker(uid) then
                player_num = player_num + 1
            end      
        end  
    end

    return player_num    
end

function internal.get_user_playing_info()
    local playing_info = {}
    playing_info.game_type = self_game_type
    playing_info.table_type = self_table_type
    playing_info.total = get_non_robot_players_num()

    return playing_info
end

local function get_all_bet_list()
    local all_bet_list = {}

    if not curr_kpqz_instance then
        return all_bet_list
    end

    local all_bet_list = curr_kpqz_instance:get_all_bet_list()
    return all_bet_list
end

local function get_bet_result_list()
    local bet_result_list = {}

    if not curr_kpqz_instance then
        return bet_result_list
    end

    if curr_status ~= GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE then
        return bet_result_list
    end

    return game_bet_result_list
end

local function get_all_five_cards(side_cards_map, side)
    local self_card_list = side_cards_map[side]
    return self_card_list
end

local function get_self_card_list(uid)
    local self_card_list = {}

    if not curr_kpqz_instance then
        dbglog(string_format("-----------1------uid(%d)-------------------------", uid))
        return self_card_list
    end

    if not curr_kpqz_instance:check_player_playing(uid) then
        dbglog(string_format("-----------2------uid(%d)-------------------------", uid))
        return self_card_list
    end

    local side_cards_map = curr_kpqz_instance:get_side_cards_map()
    local player_data_map = curr_kpqz_instance:get_player_data_map()
    local player_data = player_data_map[uid]

    if curr_status == GAME_STATUS.GAME_STATUS_START or curr_status == GAME_STATUS.GAME_STATUS_BET then
      self_card_list = get_first_three_cards(side_cards_map, player_data.side)
    elseif curr_status == GAME_STATUS.GAME_STATUS_CACL_CATTLE or curr_status == GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE then
        self_card_list = get_all_five_cards(side_cards_map, player_data.side)
    end

    dbglog(string_format("========================>uid(%d) self_card_list(%s)", uid, tostring_r(self_card_list)))

    return self_card_list
end

local function get_final_banker_bet_info()
    local bet_info = {}

    if not curr_kpqz_instance then
        return bet_info
    end

    local banker_info = curr_kpqz_instance:get_banker_info()
    return { uid = banker_info.banker_uid, banker_times = banker_info.banker_times }
end

local function get_left_time_and_game_status()
    local lefe_time = 0
    local next_status_time = 0
    local curr_time = util.get_now_time()
    local client_status = GAME_STATUS.GAME_STATUS_INIT

    if curr_status == GAME_STATUS.GAME_STATUS_INIT then
        lefe_time = 0
        client_status = GAME_STATUS.GAME_STATUS_INIT
    elseif curr_status == GAME_STATUS.GAME_STATUS_WAIT_START then
        next_status_time = game_start_wait_start_time + GAME_TIME_WAIT_START
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS.GAME_STATUS_WAIT_START
    elseif curr_status == GAME_STATUS.GAME_STATUS_START then
        next_status_time = game_start_start_time + GAME_TIME_START
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS.GAME_STATUS_START
    elseif curr_status == GAME_STATUS.GAME_STATUS_BET then
        next_status_time = game_start_bet_time + GAME_TIME_BET
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS.GAME_STATUS_BET
    elseif curr_status == GAME_STATUS.GAME_STATUS_CACL_CATTLE then
        next_status_time = game_start_calc_cattle_time + GAME_TIME_CACL_CATTLE
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS.GAME_STATUS_CACL_CATTLE
    elseif curr_status == GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE then
        next_status_time = game_start_show_and_settle_time + GAME_TIME_SHOW_AND_SETTLE
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE
    else
        errlog(string_format("curr_status(%d) error", curr_status))
    end

    if lefe_time < 0 then
        lefe_time = 0
    end
    
    dbglog(string_format("curr_status(%d) left_time(%d)", curr_status, lefe_time))
    return lefe_time, client_status
end

local function get_grab_banker_list()
    local grab_banker_list = {}

    if not curr_kpqz_instance then
        return grab_banker_list
    end

    local player_data_map = curr_kpqz_instance:get_player_data_map()

    for uid, player_data in pairs(player_data_map) do
    	if player_data.banker_times ~= 0 then
        	local bet_info = { uid = player_data.uid, banker_times = player_data.banker_times }
        	table_insert(grab_banker_list, bet_info)
        end
    end

    return grab_banker_list
end

local function get_calc_cattle_end_list()
    local end_list = {}

    if not curr_kpqz_instance then
        return end_list
    end

    local player_data_map = curr_kpqz_instance:get_player_data_map()

    for uid, player_data in pairs(player_data_map) do
        if player_data.calc_end then
            table_insert(end_list, uid)
        end
    end

    return end_list
end

local function get_game_status_info(uid)
    local game_status_info = {}
    local lefe_time, client_status = get_left_time_and_game_status()

    game_status_info.left_time = lefe_time
    game_status_info.curr_status = client_status
    game_status_info.sitdown_coins = self_game_config.min
    game_status_info.all_bet_list = get_all_bet_list()
    game_status_info.grab_banker_list = get_grab_banker_list()
    game_status_info.bet_result_list = get_bet_result_list()
    game_status_info.self_card_list = get_self_card_list(uid)
    game_status_info.calc_cattle_end_list = get_calc_cattle_end_list()       
    game_status_info.banker_bet = get_final_banker_bet_info()
    game_status_info.banker_max_times = get_grab_banker_max_times(uid)
    
    return game_status_info
end

function get_game_detail(uid)
    local game_detail = {}

    game_detail.result = error_code.RSP_SUCESS
    game_detail.player_info_list = get_rsp_seat_player_info_list()
    game_detail.game_status = get_game_status_info(uid)

    return game_detail
end

--资源加载完成后请求动态数据
function handler.REQ_GAME_DETAIL(uid, msg, game_session)
  --  dbglog(string_format("uid(%d) is_robot(%s) REQ_GAME_DETAIL", uid, tostring(is_robot(uid))))

    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_GAME_DETAIL", { result = ret })
        return true
    end

    local game_detail = get_game_detail(uid)

    send_to_gateway(uid, game_session, "kpqz.RSP_GAME_DETAIL", game_detail)
    return true
end

local function is_times_in_grab_list(banker_times)
    local grab_list = constant.KPQZ_NN_GRAB_BANKER_TIMES_LIST
    for _,times in pairs(grab_list) do
        if banker_times == times then
            return true
        end
    end

    return false
end

local function check_grab_banker_req_param(uid, game_session, banker_times)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if not curr_kpqz_instance then
        errlog("curr_kpqz_instance is nil uid:", uid)
        return false, error_code.GRAB_BANKER_DEADLINE_ERROR
    end

    if banker_times > get_grab_banker_max_times(uid) then
        errlog("banker time error",banker_times,uid)
        return false,error_code.RSP_FAIL
    end

    if banker_times ~= 0 and not is_times_in_grab_list(banker_times) then
        errlog("banker time error",banker_times,uid)
        return false,error_code.RSP_FAIL
    end

    return true
end

function handler.REQ_GRAB_BANKER(uid, msg, game_session)
    local banker_times = msg.banker_times
  --  dbglog(string_format("uid(%d) banker_times(%d) is_robot(%s) REQ_GRAB_BANKER", uid, banker_times, tostring(is_robot(uid))))

    local ok, ret = check_grab_banker_req_param(uid, game_session,banker_times)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_GRAB_BANKER", { result = ret })        
        return true
    end 

    local ok, ret = curr_kpqz_instance:grab_banker_action(uid, banker_times, kpqz.PLAYER_ACTION)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_GRAB_BANKER", { result = ret })        
        return true
    end 

    send_to_gateway(uid, game_session, "kpqz.RSP_GRAB_BANKER", { result = error_code.RSP_SUCESS })
    notify_all("kpqz.NTF_GRAB_BANKER", { uid = uid, banker_times = banker_times })    
    return true
end

local function check_cacl_cattle_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    if not curr_kpqz_instance then
        errlog("curr_kpqz_instance is nil uid:", uid)
        return false, error_code.SHOW_CARD_DEADLINE_ERROR
    end

    return true
end

function handler.REQ_CACL_CATTLE(uid, msg, game_session)
  --  dbglog(string_format("uid(%d) is_robot(%s) REQ_CACL_CATTLE", uid, tostring(is_robot(uid))))

    local ok, ret = check_cacl_cattle_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_CACL_CATTLE", { result = ret })        
        return true
    end

    local ok, ret = curr_kpqz_instance:cacl_cattle_action(uid, kpqz.PLAYER_ACTION)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_CACL_CATTLE", { result = ret })        
        return true
    end

    send_to_gateway(uid, game_session, "kpqz.RSP_CACL_CATTLE", { result = error_code.RSP_SUCESS })
    notify_all("kpqz.NTF_CACL_CATTLE", { uid = uid })
    return true
end

local function is_times_in_bet_list(bet_times)
    for _,times in pairs(sorted_can_bet_times_list) do
        if times == bet_times then
            return true
        end
    end
    return false
end

local function check_bet_req_params(uid, game_session, bet_times)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if not curr_kpqz_instance then
        errlog("curr_kpqz_instance is nil uid:", uid)
        return false, error_code.BET_DEADLINE_ERROR
    end

    if not is_times_in_bet_list(bet_times) then
        errlog("banker bet error",bet_times,uid)
        return false,error_code.RSP_FAIL
    end
    return true
end

function handler.REQ_BET(uid, msg, game_session)
    local bet_times = msg.bet_times
  --  dbglog(string_format("uid(%d) bet_times(%d) is_robot(%s) REQ_BET", uid, bet_times, tostring(is_robot(uid))))


    local ok, ret = check_bet_req_params(uid, game_session, bet_times)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_BET", { result = ret })
        return true
    end
    
    local ok, ret = deal_bet_logic(uid, bet_times, kpqz.PLAYER_ACTION)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_BET", { result = ret })
        return true
    end

    send_to_gateway(uid, game_session, "kpqz.RSP_BET", { result = error_code.RSP_SUCESS })
    return true
end

local function check_sitdown_req_param(uid, game_session, position)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end   

    if seated_position_uid_map[position] ~= false then
        errlog(string_format("uid(%d) position(%d) SEAT_IS_EMPLOY", uid, position))
        return false, error_code.SEAT_IS_EMPLOY
    end

    local player_info = player_info_list[uid]

    local ok, ret = check_sit_down_coins(uid, player_info.coins)
    if not ok then
        return false, ret
    end

    if player_info.position then
        errlog(string_format("uid(%d) SEAT_FAIL_HAVE_SEAT", uid))
        return false, error_code.SEAT_FAIL_HAVE_SEAT
    end

    return true
end

function handler.REQ_SITDOWN(uid, msg, game_session)
    local position = msg.position
    dbglog(string_format("uid(%d) position(%d) is_robot(%s) REQ_SITDOWN", uid, position, tostring(is_robot(uid))))

    local ok, ret = check_sitdown_req_param(uid, game_session, position)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_SITDOWN", { result = ret })
        return true
    end

    deal_sit_down(uid, position)
    send_to_gateway(uid, game_session, "kpqz.RSP_SITDOWN", { result = error_code.RSP_SUCESS })
    return true
end

local function player_is_playing_game(uid)
    if not curr_kpqz_instance then
        return false
    end

    return curr_kpqz_instance:check_player_playing(uid)
end

local function check_standup_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if player_is_playing_game(uid) then
        errlog(string_format("uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR", uid))
        return false, error_code.CANT_STANDUP_DURING_PLAYING_ERROR
    end

    local player_info = player_info_list[uid]
    local position = player_info.position

    if not position then
        errlog(string_format("uid(%d) not in position UP_FAIL_NOT_HAVE_SEAT", uid))
        return false, error_code.UP_FAIL_NOT_HAVE_SEAT
    end

    if seated_position_uid_map[position] == false then
        errlog(string_format("uid(%d) not in position(%d) UP_FAIL_NOT_HAVE_SEAT", uid, position))
        return false, error_code.UP_FAIL_NOT_HAVE_SEAT
    end

    if seated_position_uid_map[position] ~= uid then
        errlog(string_format("uid(%d) not in position(%d) UP_FAIL_NOT_HAVE_SEAT", uid, position))
        return false, error_code.UP_FAIL_NOT_HAVE_SEAT
    end

    return true
end

function handler.REQ_STANDUP(uid, msg, game_session)
    dbglog(string_format("uid(%d) is_robot(%s) REQ_STANDUP", uid, tostring(is_robot(uid))))

    local ok, ret = check_standup_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_STANDUP", { result = ret })
        return true    
    end

    deal_stand_up(uid, 0)
    send_to_gateway(uid, game_session, "kpqz.RSP_STANDUP", { result = error_code.RSP_SUCESS })
    return true
end

function handler.REQ_CHAT(uid, msg)
 --   dbglog(string_format("uid(%d) REQ_CHAT", uid))

    local player_info = player_info_list[uid]
    local curr_time = util.get_now_time()

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, uid_game_session_map[uid], "table.RSP_CHAT", { result = ret })
        return true
    end

    if player_info.last_chat_time and curr_time - player_info.last_chat_time < 1 then
        send_to_gateway(uid, uid_game_session_map[uid], "table.RSP_CHAT", { result = error_code.REQ_CHAT_TOO_FAST })
        return true
    end

    player_info.last_chat_time = curr_time
    send_to_gateway(uid, uid_game_session_map[uid], "table.RSP_CHAT", { result = error_code.RSP_SUCESS })

    local ntf = { 
        uid = uid, 
        type = msg.type, 
        picture_id = msg.picture_id, 
        content = msg.content, 
        voice = msg.voice, 
        content_id = msg.content_id, 
        name = player_info.name, 
        icon = player_info.icon, 
        sex = player_info.sex, 
     }

    notify_all("table.NTF_CHAT", ntf)

    return true
end

function handler.REQ_LEAVE(uid, msg, game_session)
    local robot = is_robot(uid)
    --dbglog(string_format("uid(%d) is_robot(%s) REQ_LEAVE", uid, tostring(is_robot(uid))))

    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = ret })
        return true
    end

    local init_coins = player_info_list[uid].init_coins or 0
    if player_is_playing_game(uid) then
        dbglog(uid,"uid during game CANT_STANDUP_DURING_PLAYING_ERROR")
		send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = error_code.RSP_SUCESS })
        uid_game_session_map[uid] = OFFLINE_CLIENT_FD
        skynet.send(".table_mgr", "lua", "leave", uid) 
        watch_session(game_session, uid, false)
        if not robot and curr_status >= GAME_STATUS.GAME_STATUS_SHOW_AND_SETTLE then
            R().hallsvr({key=uid}):send('.msg_handler', 'check_lucky_tigger',uid,self_game_type,init_coins)
        end

        return true
    end

    deal_player_regular_leave(uid)    
    send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = error_code.RSP_SUCESS })
    watch_session(game_session, uid, false)

    if not robot then
        R().hallsvr({key=uid}):send('.msg_handler', 'check_lucky_tigger',uid,self_game_type,init_coins)
    end

    return true
end

local function check_table_change_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end    

    if player_is_playing_game(uid) then
        errlog(string_format("uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR", uid))
        return false, error_code.IN_GAME_CAN_NOT_CHANGE
    end

    local player_info = player_info_list[uid]

    --可以换桌了 先通知玩家离开
    if player_info.coins < self_game_config.min then
        errlog(string_format("uid(%d) coins(%d) min(%d)", uid, player_info.coins, self_game_config.min))
        return false, error_code.GOLD_IS_NOT_ENOUGH
    end

    return true
end

--换桌
function handler.REQ_TABLE_CHANGE(uid, msg, game_session)
    dbglog(string_format("uid(%d) is_robot(%s) REQ_TABLE_CHANGE", uid, tostring(is_robot(uid))))

    local ok, ret = check_table_change_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_TABLE_CHANGE", { result = ret })
        return true
    end

    local player_info = player_info_list[uid]
    local change_table_type
    local table_data = global_configs.roomdata[self_table_type]
    --优先满足在当前场次换桌
    if player_info.coins >= table_data.min then
        if table_data.max == 0 then --无限大
            change_table_type = self_table_type
        end
        if table_data.max ~= 0 and player_info.coins <= table_data.max then
            change_table_type = self_table_type
        end
    end

    for table_type,table_data in pairs(global_configs.roomdata) do
        if change_table_type then
            break
        end
        local game_type = game_def.get_game_type(table_type)
        if game_type == self_game_type and player_info.coins >= table_data.min then
            if table_data.max == 0 or (table_data.max ~= 0 and player_info.coins <= table_data.max) then
                change_table_type = table_type
            end
        end
    end
    if not change_table_type then
        send_to_gateway(uid,game_session,'table.RSP_TABLE_CHANGE',{result = error_code.GOLD_IS_NOT_ENOUGH})
        return  
    end
    print("change_table_type=============",change_table_type)

    local roomdata = global_configs.roomdata[change_table_type]
    local user_data = {
        uid = uid,
        table_type = change_table_type,
        begin_time = util.get_now_time(),
        name = player_info.name,
        coins = player_info.coins,
        sex = player_info.sex,
        icon = player_info.icon,
        old_table_gid = self_table_gid,
        ip_limit = roomdata.ip_limit or false,
        login_ip = player_info.login_ip,
    }   

    deal_player_regular_leave(uid)

    --请求匹配服进行匹配了
    local ok = R().newmatchsvr(1):call(".table_mgr", "match_player", user_data)
    if not ok then
        errlog(string_format("uid(%d) call newmatchsvr table_mgr match_player error", uid))
        send_to_gateway(uid, game_session, "table.RSP_TABLE_CHANGE", { result = error_code.RSP_FAIL })
        return true
    end

    send_to_gateway(uid, game_session, "table.RSP_TABLE_CHANGE", { result = error_code.RSP_SUCESS })
    return true
end

--换游戏
function handler.REQ_GAME_CHANGE(uid,msg,game_session)
    local roomdata_list = global_configs.roomdata or {}
    local roomdata = roomdata_list[msg.table_type]
    if not roomdata then
        errlog(self_table_type,"check_game_change_table but roomdata is nil")
        return false
    end

    local player_info = player_info_list[uid]
    if not player_info then
        dbglog("REQ_GAME_CHANGE error player_info is nil", uid)
        send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.PERMISSION_DENIDE})
        return false
    end

    --游戏中不可以换游戏
    if player_is_playing_game(uid) then
        errlog(string_format("uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR",uid))
        send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.IN_GAME_CAN_NOT_CHANGE})
        return false
    end

    if player_info.coins < (roomdata.min or 0) then
        send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.GOLD_IS_NOT_ENOUGH})
        return false
    end

    if (roomdata.max or 0) ~= 0 and player_info.coin > (roomdata.max or 0) then
        send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.GOLD_OUT_OF_MAX})
        return false
    end
    print("change_table_type=============",msg.table_type)

    local user_data = {
        uid = uid,
        table_type = msg.table_type,
        begin_time = util.get_now_time(),
        name = player_info.name,
        coins = player_info.coins,
        sex = player_info.sex,
        icon = player_info.icon,
        old_table_gid = self_table_gid,
        ip_limit = roomdata.ip_limit or false,
        login_ip = player_info.login_ip,
    }   

    deal_player_regular_leave(uid) --离开

    --请求匹配服进行匹配了
    local ok = R().newmatchsvr(1):call(".table_mgr", "match_player", user_data)
    if not ok then
        errlog(string_format("uid(%d) call newmatchsvr table_mgr match_player error",uid))
        send_to_gateway(uid, game_session, "table.RSP_GAME_CHANGE", { result = error_code.RSP_FAIL })
        return true
    end
    send_to_gateway(uid, game_session, "table.RSP_GAME_CHANGE", { result = error_code.RSP_SUCESS })

    return true
end

local function check_have_enough_coins(uid,coins)
    local player_info = player_info_list[uid]
    if not player_info or player_info.coins < coins then
        return false
    end
    if curr_status == GAME_STATUS.GAME_STATUS_BET or curr_status == GAME_STATUS.GAME_STATUS_CACL_CATTLE then
        local bet_info =  curr_kpqz_instance:get_my_bet_list(uid)
        if player_info.coins - bet_info.bet_times * 3 < coins then 
            return false
        end
    end
    return true
end

local function caishen_coins(uid, coins, award_coins, ...)
    local robot = is_robot(uid)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "caishen_coins", uid, coins, award_coins, {is_robot=robot},get_extra_bill_info(uid))
    if not ok then
        errlog(string_format("uid(%d) call basesvr caishen_coins coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr caishen_coins coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

local function pay_caishen_coins(uid,coins)
    local caishenjiadao_store = global_configs.caishenjiadao_store
    local fee_rate = caishenjiadao_store.fee_rate
    local sysback_coins = math_floor((coins *fee_rate) / 10000)
    local base_coins = coins - sysback_coins
    local curr_coins
    local player_info = player_info_list[uid]
    local ret_coins = system_store.check_caishen_store_result(self_table_type,uid,coins)
    player_info.all_bet = player_info.all_bet + coins
    local produce = 0
    local win_lose = 0
    if ret_coins then
        print("add coins",ret_coins)
        curr_coins = caishen_coins(uid,coins,ret_coins)
        if not curr_coins then
            errlog("reduce coins failed",ret )
            return error_code.NOT_ENOUGH_COINS
        end
        sendLoseWinToFriendServer(coins - ret_coins)
        --改变财神库存
        R().exdbsvr(1):send('.caishen_mgr','push_data_pool',self_table_type,uid,base_coins,sysback_coins)
        R().exdbsvr(1):send('.caishen_mgr','sub_caishen_base',self_table_type,uid,ret_coins)
        player_info.all_win_lose = player_info.all_win_lose - coins + ret_coins
        player_info.all_produce = player_info.all_produce + ret_coins
        produce = ret_coins       
        win_lose =  - coins + ret_coins          
    else
        local ret = reduce_coins(uid, coins, reason.OPEN_CAISHEN)
        if not ret then
            errlog("reduce coins failed",ret )
            return error_code.NOT_ENOUGH_COINS
        end
        sendLoseWinToFriendServer(coins)
        --改变财神库存
        R().exdbsvr(1):send('.caishen_mgr','push_data_pool',self_table_type,uid,base_coins,sysback_coins)
        curr_coins = ret.curr
        player_info.all_win_lose = player_info.all_win_lose - coins        
        win_lose =  - coins         
    end

    local uid = uid
    local bet_coins = coins
    local produce = produce
    local win_lose = win_lose
    local free = 0
    expand.sendSummary(self_table_type, uid, bet_coins, produce, win_lose, free)

    utils.notify_money_changed(uid,{coins = curr_coins})
    notify_all("table.NTF_MONEY_CHANGED", { chged_list = { { uid = uid, coins = curr_coins } } })
    
    update_player_coins(uid, curr_coins)
    caishen_coins_map[uid] = nil
    return error_code.RSP_SUCESS,ret_coins    
end

function handler.REQ_OPEN_CAISHEN(uid,msg)
    local coins = msg.coins
    --[[if not caishen_coins_map[uid] or caishen_coins_map[uid] ~= coins then
        errlog("put coins error",uid,caishen_coins_map[uid])
        return
    end]]
    if not caishen_coins_map[uid] or coins ~= caishen_coins_map[uid] then
        errlog("your param is error",uid,coins)
        send_to_gateway(uid,uid_game_session_map[uid],'table.RSP_OPEN_CAISHEN',{result = error_code.INPUT_ERROR})
        return false
    end

    if not check_have_enough_coins(uid,coins) then
        errlog("you have not enough coins",uid)
        send_to_gateway(uid,uid_game_session_map[uid],'table.RSP_OPEN_CAISHEN',{result = error_code.NOT_ENOUGH_COINS})
        return false
    end
    local res,ret_coins = pay_caishen_coins(uid,coins)
    if res == error_code.RSP_SUCESS then
        send_to_gateway(uid,uid_game_session_map[uid],'table.RSP_OPEN_CAISHEN',{result = res,award_coins = ret_coins})
    else
        send_to_gateway(uid,uid_game_session_map[uid],'table.RSP_OPEN_CAISHEN',{result = res})
    end
end


function handler.REQ_USE_MAGIC_PICTURE(uid, msg, game_session)
    local dst_id = msg.dst_id
    local picture_id = msg.picture_id 

    dbglog(string_format("uid(%d) dst_id(%d) picture_id(%d) REQ_USE_MAGIC_PICTURE", uid, dst_id, picture_id))
    
    local player_info = player_info_list[uid]
    if player_info and not player_info.position then
        send_to_gateway(uid, game_session, "kpqz.RSP_GAME_DETAIL", {result = error_code.USE_ITEM_NEED_POSTION})
        return true
    end

    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", { result = ret })
        return true
    end

    local dst_player_info = player_info_list[dst_id]
    if not dst_player_info or not dst_player_info.position then
        errlog(string_format("uid(%d) dst_id(%d) dst player no seat", uid, dst_id))
        send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", { result = error_code.TABLE_USE_MAGIC_PICTURE_MUST_SITDOWN })
        return true
    end

--[[
    local ret = reduce_coins(uid, constant.USE_MAGIC_PICTURE_COIN, reason.USE_MAGIC_PICTRUE, get_extra_bill_info(uid))
    if not ret then
        send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", { result = error_code.GOLD_IS_NOT_ENOUGH })
        return true
    end
--]]

    send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", {})

	local player_info = player_info_list[uid]

    notify_all("table.NTF_USE_MAGIC_PICTURE", { 
        src_id = uid, 
        dst_id = dst_id, 
        picture_id = picture_id, 
        coins = player_info.coins, 
     })


	

	--local r_list = {}
	--r_list[uid] = {curr=ret.curr}
	
	--warlog("handler.REQ_USE_MAGIC_PICTURE", tostring_r(r_list))
	
	--notify_all_coins_change(r_list)
    --utils.notify_money_changed(uid, { coins = ret.curr }, game_session)
    return true
end

function internal.disconnect(uid, game_session)
    dbglog(string_format("uid(%d) game_session(%d) disconnect", uid, game_session))

    if not game_session_correct(uid, game_session) then
        return false
    end

    if player_is_playing_game(uid) then
        errlog(string_format("uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR", uid))
        uid_game_session_map[uid] = OFFLINE_CLIENT_FD
        skynet.send(".table_mgr", "lua", "leave", uid)
        watch_session(game_session, uid, false)
        return true
    end

    deal_player_regular_leave(uid)
    watch_session(game_session, uid, false)
    return true
end

function internal.stop_server()
    stopping_server = true
end

function internal.close_server()
    print("kpqz_nntable======close_server====")
    closing_server = true
    local curr_time = util.get_now_time()
    check_close_table(curr_time)
end

function internal.enter(uid, game_session, msg)
  --  dbglog("=================11111111111enter",uid)
    local ok, ret = deal_enter(uid, game_session, msg)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_ENTER", { result = ret })
        return false
    end

    local msg = { result = error_code.RSP_SUCESS, game_type = self_game_type, table_type = self_table_type, kpqz_enter_info = get_enter_info(),name = self_game_config.name, }
    send_to_gateway(uid, game_session, "table.RSP_ENTER", msg)
    notify_player_enter(uid)

    trigger_event('on_use_picture',uid,global_configs.robot_picture.robot_enter,true)

    watch_session(game_session, uid, true)
    if not is_robot(uid) then
        R().friendsvr(1):send(".info_mgr", "update_info", uid, { curr_game_type = self_game_type, curr_table_type = self_table_type}) 
    end   
    --notify_take_compensation(uid)
    return true
end

function internal.touch(key)
end

local function get_card_type_times_map()
    local card_type_times_map = {}
    local special_card_type = constant.NN_NAME_DEFAULT_SPECIAL_CARD_TYPE_MAP

    for _, card_type in pairs(special_card_type) do
        if card_type == special_card_type.FOUR_BOMBS then
            card_type_times_map[card_type] = 4
        elseif card_type == special_card_type.FIVE_COLOR_CATTLES then
            card_type_times_map[card_type] = 5
        elseif card_type == special_card_type.FIVE_CALVES then
            card_type_times_map[card_type] = 8
        end
    end

    for card_type = 0, 10 do
        if card_type <= 6 then
            card_type_times_map[card_type] = 1
        elseif card_type >= 7 and card_type <= 9 then
            card_type_times_map[card_type] = 2
        elseif card_type == 10 then
            card_type_times_map[card_type] = 3
        end
    end

    return card_type_times_map
end

local function sort_can_bet_times_list()
    sorted_can_bet_times_list = constant.KPQZ_NN_CAN_BET_TIMES_LIST
    table_sort(sorted_can_bet_times_list)
end

local function init_grab_banker_times_dizhu_map()
    local grab_banker_times = self_game_config.grab_banker_times

    for can_times, config_string in pairs(grab_banker_times) do
        local t = return_comma_split(config_string, ",")
        grab_banker_times_dizhu_map[can_times] = t
    end
end

function internal.start(conf)
    tablesvr_id = tonumber(skynet.getenv "server_id")
    self_table_id = conf.table_id
    self_table_type = conf.table_type
    self_table_gid = conf.table_gid
    self_game_type = conf.game_type
    self_table_pattern = conf.table_conf.table_pattern   --记录了桌子的模式以及机器人的数量robot_num,table_pattern
    local table_conf = conf.table_conf

    self_game_config = global_configs.roomdata[self_table_type]
    curr_round = 0
    curr_round_need_player = 0
    max_seat_num = table_def.game_player_num[self_game_type]

    for i = 1, max_seat_num do
        table_insert(seated_position_uid_map, false)
    end

    init_grab_banker_times_dizhu_map()
    sort_can_bet_times_list()
    game_start_init_time = util.get_now_time()
    game_start_wait_start_time = util.get_now_time()
    card_type_times_map = get_card_type_times_map()

    dbglog(string_format("conf(%s) \nid<%d>, type(%d), gid(%d)", tostring_r(conf), self_table_id, self_table_type, self_table_gid))
    -- if table_conf.robot_num and table_conf.robot_num > 0 then
    --     add_robot(table_conf.robot_num)
    -- end
    self_table_pattern = table_conf.table_pattern

    skynet.fork(game_update)
    return true
end

function internal.exit()
    skynet.exit()
end

function internal.update_coins_on_table(uid, amount,safe_box)
    dbglog(string_format("uid(%d) amount(%d) update coins", uid, amount))

    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = player_info.coins + amount
        local chged_list = {}
        table_insert(chged_list,{uid=uid,coins=player_info.coins})
        if player_info.position then
            notify_all('table.NTF_MONEY_CHANGED',{chged_list=chged_list})
        else
            send_to_gateway(uid,uid_game_session_map[uid],'table.NTF_MONEY_CHANGED',{chged_list = chged_list})
        end
        if safe_box then
            player_info.safe_box = safe_box
        end
    end
    return true
end

function internal.newmatch_register_all(player_data_list)
    local result = {}
    local failed_list = {}
    local success_list = {}

    dbglog("=======================111111newmatch,self_table_pattern",self_table_pattern)
    local end_idx
    on_matching = true
    for i, player_data in pairs(player_data_list) do
        local uid = player_data.uid
        if uid_game_session_map[uid] then
            dbglog(uid, "have been already in table", i)
            table_insert(success_list, uid)
            goto continue
        end

        assert(not player_info_list[uid])
        local player_info = get_player_enter_data(uid)
        player_info.login_ip = player_data.login_ip

        if not player_info then
            table_insert(failed_list, player_data)
            goto continue
        end

        if not lock_one_player(uid) then
            table_insert(failed_list, player_data)
            goto continue
        end

        if get_seated_num() >= table_def.game_player_num[self_game_type] then
            end_idx = i
            unlock_one_player(uid)
            break
        end

        player_info.all_win_lose = 0
        player_info.all_free = 0
        player_info.all_produce = 0
        player_info.all_bet = 0


        player_info.init_coins = player_data.coins
        player_info_list[uid] = player_info
        add_to_noseat(uid)
        dbglog("-----------call----add_to_noseat-------------------")
        uid_game_session_map[uid] = REGISTER_CLIENT_FD

        table_insert(success_list, uid)

        player_register_time_map[uid] = util.get_now_time()

        dbglog("new_player:", self_table_id, uid)
        players_never_start[uid] = true

        ::continue::
    end

    if end_idx then
        for i = end_idx, #player_data_list do
            local player_data = player_data_list[i]
            table_insert(failed_list, player_data)
        end
    end
    on_matching = false

    result.failed_list = failed_list
    result.success_list = success_list
    local table_stats = {}
    table_stats.table_type = self_table_type
    table_stats.left_num = table_def.game_player_num[self_game_type] - get_seated_num()
    table_stats.table_gid = self_table_gid
    table_stats.game_type = self_game_type
    table_stats.robot_num = get_seated_robot_num()
    table_stats.human_num = get_seated_human_num()
    table_stats.players_never_start = players_never_start
    table_stats.table_pattern = self_table_pattern
    table_stats.player_ip_list = get_player_ip_list()
    result.table_stats = table_stats

    return result
end


--在线玩家概况数据
function internal.get_summary_online()
    local res = {}
    for uid, player in pairs(player_info_list) do 
        if not is_robot(uid) then 
            table_insert(res, {
                uid = uid, 
                all_bet = player.all_bet, --总投注
                all_win_lose = player.all_win_lose, --输赢
                all_free = player.all_free, --台费
                all_produce = player.all_produce, --总产出
            })
        end
    end
    return res
end









--=================================table protocols=================
local function update_player_heartbeat(uid)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.last_ping_time = util.get_now_time()
    end
end

local function get_msg_module_name(msgid)
    local m = msgdef.id_to_name[msgid]

    if not m then 
        return false, false
    end

    return m[1], m[2] --[1]->module, [2]->name
end

function dispatch_client_message(game_session, uid, msg, size)
    local _, msgid, pbmsg, pbsize = proxypack.unpack_client_message(msg, size)
    local module, name = get_msg_module_name(msgid)

    if not module or not name then
        dbglog(string_format("msgid(%d) module(%s) name(%s) invalid msgid", msgid, module, name))
        return false
    end

    local pbname = module .. "." .. name
    local req_msg = pb.decode(pbname, pbmsg, pbsize)

--    dbglog(string_format("address(%s) uid(%s) pbname(%s) content(%s)", 
 --       skynet.address(skynet.self()), tostring_r(uid), pbname, tostring_r(req_msg)))

    local f = handler[name]
    if not f then
        errlog(string_format("pbname(%s) unknown action", pbname))
        return false
    end

    local ret = f(uid, req_msg, game_session)
    if not ret then
        errlog(string_format("failed to handle requrest(%s.%s)", module, name))
        return false
    end

    update_player_heartbeat(uid)
    return true
end

local function dispatch(_, _, game_session, uid, data)
    local msg, size = proxypack.pack_raw(data)
    local ok, errmsg = xpcall(dispatch_client_message, debug.traceback, game_session, uid, msg, size)
    skynet.trash(msg, size)  --这里需要保证内存被释放
    if not ok then 
        errlog(errmsg)
    end
end

function handle_lua_req(session,source,action,...)
 --   dbglog("internal request", action, ...)
    local f = internal[action]
    if f then
        skynet.retpack(f(...))
    else
        handler[action](...)
    end
end

 function start()
    skynet.register_protocol { 
        name = "client", 
        id = skynet.PTYPE_CLIENT, 
        unpack = skynet.unpack, 
        dispatch = dispatch
    }
    
    skynet.start(function()
        skynet.dispatch("lua", function(session, source, action, ...)
            handle_lua_req(session,source,action,...)
        end)

        load_config()
    end)
end

return _ENV
